Wake On Lan - For those running home assistant in docker

If you run home assistant in docker and are not using network mode host, then you can’t send magic wakeonlan packets out from homeassistant inside the docker. My solution is to create a user on your host server called “hawol” and it only has ssh access to a single command to send magic packet.

Solution as follows:

Check the wakeonlan command works on your server (it does in ubuntu 18.04)
$ wakeonlan ma:ca:dd:re:ss
If wakeonlan is an invalid command then you can install etherwake instead

Create a user on your server for the wakeonlan
$ sudo adduser hawol

Change your docker-compose or docker-run for home assistant and add the following volume mapping so that the ssh key is persistant when home assistant is upgraded (similar to the /config directory).

# Docker-compose
volumes:
  - /root/.ssh:/some_direcotry

Now get a terminal inside the homeassistant docker
$ docker exec -it homeassistant bash
(or use portainer, click homeassistant container, then click the >_ console button )

Now create a directory in the home assistant docker container to store the ssh key
# mkdir /config/.ssh/

Now create an ssh key inside the homeassistant docker container (more info)
# ssh-keygen -t rsa -b 4096 -C "[email protected]"
Save the passkey in the default directory (press enter)
Choose the passphrase as nothing (press enter twice)

Now copy to ssh key from inside the docker container on to the server
$ ssh-copy-id [email protected]

Now verify you can ssh into your server from inside homeassistant docker without a password by typing
# ssh [email protected]

Now as the root user on the main server edit the file \home\hawol\.ssh\authorized_keys
It will be a one line file and look like this ssh-rsa AAA... one very long key
Change the start of the single line in the file by adding the following bit at the start
command="/usr/bin/wakeonlan ma:ca:dd:re:ss" ssh-rsa AAAQIUHINEFAS...

Now get back inside the home assistant container check that it works by doing the following

[email protected]:~# docker exec -it homeassistant bash
bash-5.0# ssh [email protected]
Sending magic packet to 255.255.255.255:9 with ma:ca:dd:re:ss
Connection to server.ip.address closed.
bash-5.0# exit
exit
[email protected]:~#

You can use the following yaml

switch:
  - platform: command_line
    switches:
      desktop_pc:
        friendly_name: Desktop PC
        command_off: "/usr/bin/curl http://desktop.ip.address:7760/suspend"
        command_on: "/usr/bin/ssh [email protected]"
        value_template: "{{ states('binary_sensor.desktop_pc') }}"
        
binary_sensor:
  - platform: ping
    name: desktop_pc
    host: desktop.pc.ip.address
    scan_interval: 2
    count: 2
    
homeassistant:
  customize:
    switch.desktop_pc:
      assumed_state: false

The program I run on my Windows 10 Desktop is http://www.ireksoftware.com/SleepOnLan/
It can run in the system tray, it listens on port 7760 for an http command (suspend, hibernate, logoff, poweroff, forcepoweroff, lock, reboot, test)

3 Likes

Don’t you need to copy your generated SSH identity to the exterior hosts authorized keys file? That step seems to be missing from your instructions.

No i definitely didn’t have to do that. I think it happens automatically.

Edit: actually maybe i did miss that step.

Hmm. That seems to be a potential security risk. Though I admit, I don’t have this type setup to test. Curious to hear from others.

I guess if someone gets inside your home-assistant docker then they could ssh into your server as the user?

The user wouldn’t be root though, Could you create a user with very little access?

Thank you for your guidance. But to be honest, I don’t think it is better or safer than using the Network Mode Host or giving the container its own IP address via macvlan?

I have very limited knowledge.
I do know that network mode host doesn’t work well for docker if you are running reverse_proxy and other services as well.
Yes macvlan would be better.

Macvlan or Host Network will work perfectly with a proxy if you assign both networks (and the proxy’s) to the HA container.
This is how it looks like in my case in combination with Macvlan and Traefik as proxy:

  homeassistant:
    container_name: home-assistant
    image: homeassistant/home-assistant
    networks:
      mynet:
        ipv4_address: 192.168.178.203
      proxy:
    logging:
      driver: "fluentd"
      options:
        fluentd-address: 172.20.0.6:24224
        tag: docker.homeassistant
    depends_on:
      - "timescale"
      - "esphome"
      - "mqtt"
      - "reverse-proxy"
      - "deconz"
    restart: always
    volumes:
      - /srv/dev-disk-by-id-ata-ST32000542AS_5XW2HFG7/data/docker_data/homeassistant:/config
      - /etc/localtime:/etc/localtime:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "t[email protected]docker"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.routers.homeassistant.rule=Host(`MYWEBADRESS.de`)"
      - "traefik.http.routers.homeassistant.entrypoints=websecure"
      - "traefik.http.routers.homeassistant.tls.certresolver=mytlschallenge"
      - "traefik.http.services.homeassistant.loadbalancer.server.port=8123"
      - "traefik.docker.network=proxy"
    ports:
      - "8123:8123"

I use letsencrypt so would have to change the subdomain.conf file and have not quite figured out that part.

The Traefik Proxy automatically generates SSL certificates via Letsencrypt for all domains :wink:

What do you mean by subdomain.conf?

I don’t use Traefik.

I’ve updated my post so that the ssh user just runs one commands then exits, it doesn’t have access to do anything else.

I just followed the Home Assistant guide and then adapted the command within the configuration.yaml file

## wol ##

wake_on_lan:

shell_command:
  turn_off_rocky: "ssh -i /config/.ssh/id_rsa -o StrictHostKeyChecking=no  [user]@192.168.1.1 'sudo systemctl start systemd-suspend.service'"

I also used the following command to copy the contents of the /config/.ssh folder out of the container for persistence:

sudo podman cp hassos:./.ssh/ /home/hassos/config/.ssh/

in terms of the question raised by @brahmafear regarding the authrozed_keys file - ssh-copy-id does exactly that, it copies the generated public key to the authorized_keys file of your target node and saves you having to cat, copy, vi etc.

I wanted to post my reply in this old thread to share my solution to this, mainly because this was my first hit when searching for a solution to wake-on-lan from Docker.

I don’t want to use host network, and I was reluctant to use the ssh solution presented here (good effort and documentation though!).

My solution was to use named pipe, inspired by this answer from Stackoverflow
I also took inspiration from this thread with how to structure the switch on_command.

I had never used named pipes prior to this, so I won’t go into detail how they work. See the Stackoverflow post above for some more explanation, or do a quick google.
For our purposes, they are a way of passing information from docker to the controlling host. We will use the ability of wakeonlan to read the mac-adress from a file, that file will be our named pipe which will just be waiting for us to input the mac-adress to it.

Howto (tested on Ubuntu Server 18.04)
Install wakeonlan if you don’t have it already
sudo apt install wakeonlan

Create a named pipe. You can create it anywhere. If you choose a new location you must expose that location to your home assistant container.
I found it simplest to just create the pipe in home assistants config directory, remember to change the path to your own:
sudo mkfifo /path/to/homeassistant/config/wolpipe

Normally the pipe would be created with correct permission, to set them properly just in case use the command below (probably optional):
sudo chmod 644 /path/to/homeassistant/config/wolpipe

Create a systemd-service to continously listen for WOL-requests from the named pipe.
Remember to change the path to your homeassistant config directory.
Note that the service will be running as user nobody which should severely limit what a potential attacker could do on your docker host even if attacker gains access to your docker container

sudo cat > /etc/systemd/system/docker-wol.service << EOF
[Unit]
Description=Listen for wake-on-lan from docker via pipe

[Service]
Restart=always
User=nobody
Group=nogroup
ExecStart=/usr/bin/wakeonlan -f /path/to/homeassistant/config/wolpipe

[Install]
WantedBy=multi-user.target
EOF

Reload systemd, start the service and set the service to start on boot (if that’s what you want)

sudo systemctl daemon-reload
sudo systemctl start docker-wol
sudo systemctl enable docker-wol

At this point you should be able to send the mac-adress through the pipe and have your appliance turn on, to test:
sudo echo 'ma:ca:dd:re:ss' > /path/to/homeassistant/config/wolpipe

To verify, check your appliance and the logs of the service you created
journalctl -fu docker-wol

If you repeat the above test it should keep working since systemd restarts the command everytime it completes.

Now you can do the same thing from inside the home-assistant docker container.
To test communication from inside docker, get a shell on the container:
docker exec -it homeassistant bash

Run the same echo command there but use the path inside the container:
echo 'ma:ca:dd:re:ss' > /config/wolpipe

It should still work.

Finally some inspiration to what I did to get my LG WebOS TV to work with wake-on-lan from docker, in configuration.yaml (Edited for version 2022.2.0)

#wake_on_lan: # Home assistant wol support not needed anymore

webostv:
  host: !secret tvrum_ip
  name: TV-rum
  customize:
    sources:
      - MySources

shell_command:
  tvrum_wol: !secret tvrum_command_on

# Either put this here or in your automations.yaml
automation:
  - id: 'tvrum_wol'
    trigger:
      - platform: webostv.turn_on
        entity_id: media_player.tv_rum
    action:
      - service: shell_command.tvrum_wol

media_player:

Then in secrets.yaml I put the entire command line (note that we’re using the path inside the docker container here):

tvrum_command_on: "/bin/echo 'ma:ca:dd:re:ss' > /config/wolpipe"
other_secrets: 123456

Good luck, hope this helps anyone.

2 Likes

Hi Peter, thanks for posting the namedpipes method for WOL. I’m trying to implement this but get permission denied when I try to test the new service. Any ideas?

FYI, the stack post suggested to tail the pipe and then from another terminal test, which I tried but that didn’t help.

For what it’s worth, Simon’s macvlan approach worked well for me. I can now use wakeonlan like I’m truly on my local network.