Wake-on-LAN Automation: Powering Servers On and Off with Home Assistant

Let the power-hungry box sleep until something actually needs it

I have a beefy machine in the cupboard that exists to do exactly one thing: transcode media and crunch the occasional batch job. It pulls something like 90 watts at idle, which over a year is a meaningful slice of the electricity bill for a box that’s genuinely busy maybe two hours a day. For ages I left it running because the alternative — getting up and pressing the power button when I wanted to watch something — was worse. Then I wired it into Home Assistant, and now it sleeps until it’s needed and shuts itself down when it’s idle. The savings paid for the effort in a couple of months.

Wake-on-LAN (WoL) is the magic that makes this possible. A network card, even when the machine is “off” (technically in S5 soft-off), keeps listening for a special “magic packet” addressed to its MAC. Receive that packet, and the board powers on. Home Assistant can send it on a schedule, on a button press, or — the good bit — automatically when something on the network tries to reach the sleeping server.

Advertisement

This is where most people fail, so do it first. Two places need to agree:

  1. The BIOS/UEFI. Find the setting usually called “Wake on LAN”, “Power On by PCI-E”, or “ErP” (which you want disabled — ErP/EuP energy-saving mode cuts power to the NIC and kills WoL). Enable wake, disable ErP.
  2. The OS. On Linux, the network driver often disables WoL on shutdown. Check and set it with ethtool:
$ sudo ethtool eno1 | grep Wake
        Supports Wake-on: pumbg
        Wake-on: d                 # 'd' = disabled, this is the problem
$ sudo ethtool -s eno1 wol g       # 'g' = wake on magic packet
$ sudo ethtool eno1 | grep Wake-on
        Wake-on: g

Because that resets on reboot, persist it with a systemd unit:

# /etc/systemd/system/wol.service
[Unit]
Description=Enable Wake-on-LAN
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/ethtool -s eno1 wol g

[Install]
WantedBy=multi-user.target

Then sudo systemctl enable --now wol.service. Note the MAC of eno1 — you’ll need it.

Add the Wake-on-LAN integration and define a switch in configuration.yaml. The clever part is giving the switch a turn-off action too — Home Assistant can’t magic-packet a machine off, so we SSH in and tell it to suspend or shut down:

switch:
  - platform: wake_on_lan
    name: Transcode Box
    mac: "a4:bb:6d:11:22:33"
    host: 192.168.1.50          # used to ping for on/off state
    turn_off:
      service: shell_command.suspend_transcode_box

shell_command:
  suspend_transcode_box: >
    ssh -i /config/.ssh/id_ed25519 -o StrictHostKeyChecking=accept-new
    [email protected] 'sudo systemctl suspend'

The host field lets Home Assistant ping the box to know whether it’s actually up, so the switch reflects real state rather than just “I sent a packet and hoped”. Give the ha-control user a tightly scoped sudoers entry — only systemctl suspend, nothing else — so a compromised Home Assistant can’t do more than nap your server.

A button is fine, but the real prize is making the box wake itself when demand appears, with no human in the loop. The pattern I use: a proxy that, on receiving a request for a service on the sleeping box, wakes it, waits for it to come up, then forwards the request. For a media server, you can approximate this entirely in Home Assistant with an automation triggered by, say, your TV turning on:

automation:
  - alias: "Wake transcode box when the living room TV turns on"
    trigger:
      - platform: state
        entity_id: media_player.living_room_tv
        to: "on"
    condition:
      - condition: state
        entity_id: switch.transcode_box
        state: "off"
    action:
      - service: switch.turn_on
        target:
          entity_id: switch.transcode_box

  - alias: "Sleep transcode box if idle for an hour"
    trigger:
      - platform: state
        entity_id: binary_sensor.transcode_box_active
        to: "off"
        for: "01:00:00"
    action:
      - service: switch.turn_off
        target:
          entity_id: switch.transcode_box

That binary_sensor.transcode_box_active is whatever signals “real work is happening” — an active Jellyfin session, a command-line job, CPU above a threshold. I derive mine from the media server’s API. When it’s been quiet for an hour, the box suspends itself. When the telly comes on, it’s awake by the time the app finishes loading.

WoL packets are layer-2 broadcasts, so they don’t cross subnets or VLANs without help — keep Home Assistant and the target on the same broadcast domain, or configure a directed broadcast. Some switches and Wi-Fi setups also drop the packet; wired is far more reliable. And if the machine wakes but the OS doesn’t come up, it’s almost always the ethtool setting reverting on the previous shutdown — the systemd unit above is what fixes that for good.

Worth it? If you have a high-draw machine that’s idle most of the day, absolutely — this is one of the few homelab projects with a tangible, recurring payoff in pounds off the bill, not just satisfaction. The setup is an evening, the hardware-enablement is the only fiddly part, and once it works it’s invisible: the box is simply there when you need it and quietly asleep when you don’t. If your server is genuinely busy around the clock, skip all this — but most homelab boxes aren’t, and they should be sleeping more than they are.

Advertisement

Related Content

Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.