Wake-on-LAN Is Your Friend When Self-Hosting: Turn Servers On and Off on Your Terms

Most of my self-hosted boxes do not need to be awake. The NAS that only I and the backup job touch, the test rig I spin up twice a week, the media server that serves exactly one household: they sit there drawing power around the clock because turning them on and off by hand was always too much friction.

Wake-on-LAN fixes that with something almost embarrassingly simple: a tiny UDP packet that a sleeping network card is still listening for. No agent, no cloud dependency, no extra service. The machine wakes itself because a box on its own network asked nicely.

This is the sysadmin read on Wake-on-LAN for self-hosters: where it actually pays off, how to automate it from Home Assistant, how it coexists with the Zigbee smart plugs you already own, how to shut a machine down gracefully when it is carrying real load, and the honest places where a magic packet will let you down.

The short version

  • A packet, not a service. A magic packet is a small UDP broadcast (six bytes of 0xFF followed by the target MAC repeated sixteen times, conventionally to port 9). The NIC's firmware watches for it while the rest of the machine sleeps.
  • BIOS first, OS second. The feature is usually off at two layers: enable Wake on LAN (or Power On by PCI-E) in firmware, then make sure the driver keeps it armed. On Linux, ethtool -s eth0 wol g turns it on, but the flag does not always survive reboots on its own.
  • Same broadcast domain or nothing. The packet is a broadcast and routers drop broadcasts at the boundary. If your waking box lives on another VLAN, something always-on on that segment (a router, a Pi, or Home Assistant itself on the right interface) has to be the sender.
  • Home Assistant turns it into an automation target. The integration, added from the UI, gives you a stateless button, and one action call (wake_on_lan.send_magic_packet) sends the packet from any automation, script, or dashboard tile. A switch with ping-tracked on/off state exists too, but only as the YAML switch platform; the no-YAML route is a template switch helper wrapped around the button.
  • Smart plugs and WoL are not rivals. The plug controls mains power; the packet controls boot. Used together, with the plug on before the packet arrives, they cover both clean boots and hung machines.
  • Never cut power to shut down. The magic packet gets a machine up; it does nothing for the descent. Graceful means SSH plus systemctl, waiting for load to drain, and treating the plug's off switch as a last resort for a machine that has already stopped.
Verified
  • Magic packet format6 bytes of 0xFF plus the target MAC repeated 16 times, sent as a UDP broadcast (conventionally port 9)
  • HomeAssistant Wake on LAN integrationas documented at home-assistant.io/integrations/wake_on_lan (2026.9 docs): UI-configured button with optional SecureOn password, wake_on_lan.send_magic_packet action, YAML-only switch with a nested turn_off action
  • Linux NIC controlethtool -s <iface> wol g and ethtool <iface> to inspect the Wol flag
  • HomeAssistant automation syntaxmodern triggers/conditions/actions schema with action: (2024.10+); legacy platform:/service: forms still accepted
  • Observed onDebian and Ubuntu servers, x86 boards, and consumer Zigbee plugs

Command shapes and integration behavior checked against the Home Assistant integration documentation (2026.9) on 2026-10-07.

I will say up front that this is not a per-board tutorial: every vendor buries the setting somewhere different, and every NIC driver handles the flag a little differently. What matters is the model: power, firmware, driver, packet, destination. Get those in the right order and the magic packet is one of the most reliable things on your network.

What Wake-on-LAN actually does

The mechanics are deliberately dumb, which is why they are reliable. A magic packet is a small UDP frame, sent to the broadcast address of the local subnet, containing six bytes of 0xFF followed by the target machine's MAC address repeated sixteen times. Nothing is authenticated because nothing needs to be: the payload is a string of bytes the NIC's firmware pattern-matches while the CPU sleeps. (Some NICs offer a SecureOn password appended to the packet — Home Assistant's setup dialog can send one — but that is a shared secret, not authentication.) Convention puts it on UDP port 9 (the old discard port), and every WoL sender I know of lets you change it if your network objects.

Because the packet is a broadcast, delivery ends at the router. That is a feature: a sleeping machine will not be dragged out of standby by some packet arriving from the internet (unless you deliberately enable directed broadcasts, which I do not recommend). The cost is that the sender usually has to live on the same broadcast domain as the target. That constraint shapes most of the deployments below.

The other half of the deal is on the machine itself. Wake-on-LAN works in the deeper sleep states (S3 suspend, S4 hibernate, S5 soft-off) where the motherboard keeps standby power flowing to the NIC. Three things have to cooperate:

  • Firmware says yes. Look for Wake on LAN, Wake on PCI-E, or a similarly named option. This is the setting most often missed, and no amount of packet-sending fixes its absence.
  • The driver keeps the NIC armed. On Linux, check the current state with ethtool eth0 (look at the Supports Wake-on and Wake-on lines) and arm it with ethtool -s eth0 wol g. Persistence varies by distro: some remember it across reboots, some quietly drop it. Two reliable anchors: a systemd-networkd .link file with WakeOnLan=magic, or on NetworkManager systems nmcli connection modify '<connection>' 802-3-ethernet.wake-on-lan magic.
  • Standby power reaches the NIC. The machine is off but the board is quietly listening. This is also the first thing a smart plug breaks: no mains power means no listening NIC, a detail that becomes central later in this post.
checking and sending from Linux
$ # inspect the NIC on the target machine
$ ethtool eth0 | grep -i wake
	Supports Wake-on: pumbg
	Wake-on: d
$ # arm it: d means disabled, g means magic packet
$ sudo ethtool -s eth0 wol g
$ # send a packet from another machine on the same VLAN
$ wakeonlan -i 192.168.10.255 aa:bb:cc:dd:ee:ff
Sending magic packet to 192.168.10.255:9 with aa:bb:cc:dd:ee:ff
$ # or from a router / always-on box with etherwake
$ etherwake -i br-lan aa:bb:cc:dd:ee:ff

Where WoL pays off when self-hosting

The value is not the packet itself, it is the decision it automates: which machines deserve to be awake right now. A few patterns earned their place in my setups:

  • On-demand servers. The media server and the photo library only serve one household. Wake on request, sleep when idle, and the box that used to hum 24/7 now does its work in the evenings. The same pattern fits a rarely used Docker or container host.
  • A backup target that wakes for its window. The machine receiving off-hours backups does not need to be up all night waiting. Wake it five minutes before the job, let it run, shut it down when the disk queue goes quiet. It slots straight into an honest 3-2-1 backup setup without adding a second always-on box.
  • Development and test rigs. A beefy box that only matters while you are working needs to be up for exactly the hours you are. Home Assistant sees you arrive or open the laptop, the boot happens during the walk to the desk.
  • Remote access to your own desktop. Combined with a self-hosted VPN box, a WoL packet sent from inside the tunnel (or relayed by something on the machine's subnet) means a powered-off desktop is still reachable. Note the boundary again: the VPN endpoint or relay has to be on the same broadcast domain as the target, which is why a small always-on device on that VLAN is such a useful pattern.
  • Recovery from a hung state. Sometimes a box is up but wedged: no SSH, no ping, console locked. The magic packet is not the only tool for that case (a power-cycle is), but the WoL-plus-plug combination below turns it into a two-stage rescue you can trigger from your phone.

Home Assistant: turning packets into automations

Home Assistant ships a dedicated Wake on LAN integration, and it has grown friendly: the integration is added through the UI (Settings, Devices and services, Add integration), and it provides a stateless wake_on_lan.send_magic_packet action you can call from anywhere. The button entity you get out of setup is stateless by design: pressing it sends the packet, but it cannot tell you whether the machine actually woke.

For a controllable switch entity with tracked state, add the switch platform instead. Mind the asymmetry: the button is the UI half of the integration, the switch is YAML-only. If you would rather not touch configuration.yaml, the docs' suggested alternative is a template switch helper — the WoL button as turn-on, a ping sensor as the state, and your shutdown action as turn-off:

# configuration.yaml (Home Assistant)
switch:
  - platform: wake_on_lan
    name: backup-target
    mac: aa:bb:cc:dd:ee:ff
    host: 192.168.10.40        # pinged to track real on/off state
    broadcast_address: 192.168.10.255
    turn_off:
      action: script.turn_on
      target:
        entity_id: script.shutdown_backup_target

The two halves matter. The host line makes Home Assistant ping the address so the switch reflects reality instead of the last thing you told it to do. The turn_off block is the honest part of the integration: there is no universal way to power a machine down remotely, so you hand it an action — here, a call to a script — that does the shutdown the right way for that box (usually over SSH, or suspend, covered below). A shell_command that SSHes directly, as in the official example, works just as well.

Automation patterns that earn their keep

  • Presence wake. Anyone comes home, the media server wakes, so by the time the couch is chosen the library is already up.
  • Scheduled windows. The backup target wakes at 03:00, the maintenance box wakes at 04:00 for updates, both get shut down by the idle logic below. Time triggers plus the switch entity are all it takes.
  • Demand-gated wake. Someone casts to the media box, or the smart home notices the first connection attempt to a sleeping service, and the wake packet goes out before the user ever sees an error. A readiness wait after the packet is essential for this one.
  • Idle down. The machine reports its own load (a lightweight agent, the Glances integration, or anything you trust), and an automation shuts it down when the load has been quiet long enough. This is the half of the pair that makes the wake automation safe to run eventually: without it, the wake and shutdown automations fight each other.
  • Keep-alive guard. While a task (a backup, a sync) is marked running, an automation re-sends the wake packet every few minutes if the ping ever drops, so the machine survives until its work finishes and you stop babysitting late-night jobs. Know its limit: a magic packet only helps from sleep or soft-off, so a machine that hard-crashed mid-job stays down until the plug rescue below kicks in. And the idle-down automation must respect the same job-running flag, or the two guards will fight over the machine.
automation:
  - alias: wake_backup_target_on_schedule
    triggers:
      - trigger: time
        at: "03:00:00"
    conditions:
      - condition: state
        entity_id: switch.backup_target
        state: "off"
    actions:
      - action: switch.turn_on
        target:
          entity_id: switch.backup_target
      - wait_template: >-
          {{ is_state('switch.backup_target', 'on') }}
        timeout: "00:03:00"

  - alias: wake_media_server_before_cast
    triggers:
      # the player leaving "off" is the earliest hint a cast is coming; if the
      # server is asleep, playback never starts, so a "playing" trigger fires
      # too late or never
      - trigger: state
        entity_id: media_player.living_room
        from: "off"
    conditions:
      - condition: state
        entity_id: binary_sensor.media_server
        state: "off"
    actions:
      - action: wake_on_lan.send_magic_packet
        data:
          mac: aa:bb:cc:dd:ee:01 # the media server, not the backup target
          broadcast_address: 192.168.10.255
      # the readiness wait the pattern demands: hold until the server answers ping
      - wait_template: >-
          {{ is_state('binary_sensor.media_server', 'on') }}
        timeout: "00:03:00"

In the cast automation, a ping binary sensor (the Ping integration, added from the UI) does the same job the host line does for the switch: it is the machine's real state, so the condition and the readiness wait both key off it.

Living with Zigbee smart plugs

A common instinct in this hobby is that the smart plug is the tool: turn the plug off, the machine is off, done. It works, and it is simple, and it is worth naming exactly what it is trading away, because the two mechanisms are near-perfect complements when you assign them the right halves of the job.

Magic packet vs smart plug for powering a server
FeatureWake-on-LANZigbee smart plug
Brings up a clean, ready machineYes: firmware boot from a genuine power-on-equivalent stateYes, but only if the plug was off; if it was on, nothing changes
Works from standbyYes: NIC keeps listening during S3/S4/S5Irrelevant: standby already means the plug is on
Removes all power drawNo: standby keeps the board aliveYes: measured zero at the socket (plus the plug's own draw)
Safe as a regular operationYes: it never interrupts a running OSRisky: a cutoff under load is a power loss event, not a shutdown
Rescues a wedged machineNo: a hung OS ignores the packetYes: a hard reset when nothing else responds
Network boundaryBroadcast domain of the target NICOnly needs Zigbee reachability
  • Brings up a clean, ready machine

    Wake-on-LAN
    Yes: firmware boot from a genuine power-on-equivalent state
    Zigbee smart plug
    Yes, but only if the plug was off; if it was on, nothing changes
  • Works from standby

    Wake-on-LAN
    Yes: NIC keeps listening during S3/S4/S5
    Zigbee smart plug
    Irrelevant: standby already means the plug is on
  • Removes all power draw

    Wake-on-LAN
    No: standby keeps the board alive
    Zigbee smart plug
    Yes: measured zero at the socket (plus the plug's own draw)
  • Safe as a regular operation

    Wake-on-LAN
    Yes: it never interrupts a running OS
    Zigbee smart plug
    Risky: a cutoff under load is a power loss event, not a shutdown
  • Rescues a wedged machine

    Wake-on-LAN
    No: a hung OS ignores the packet
    Zigbee smart plug
    Yes: a hard reset when nothing else responds
  • Network boundary

    Wake-on-LAN
    Broadcast domain of the target NIC
    Zigbee smart plug
    Only needs Zigbee reachability

The coexistence rule falls out of that table: always bring mains up before you send the packet. A NIC with no power cannot hear anything, so a plug and a wake automation fired at the same instant are a race you lose if you write the automation naive: send the packet after the plug has been on for a few seconds, never on the same instant.

The really valuable pairing is the rescue automation. Track the machine with a ping binary sensor, and let the logic be explicit: if it is unreachable for a few minutes while the plug is on, send the magic packet; if it is still unreachable after several rounds, cycle the plug off, wait a few seconds, back on, then send the packet again. That is roughly the same idea behind a hardware watchdog, just implemented with the homelab parts you already have: escalate from asking nicely to cutting power, in that order, always.

One honesty check on that last step, because it bites on real hardware. A plug cycle is a full AC loss, not a soft-off, and on many boards the NIC is not armed for WoL after total power removal until the OS has booted once. Firmware ErP/EuP "deep off" modes make it worse: they exist to keep off-mode draw under the EU's roughly-half-a-watt limit, and they do it by cutting standby power to the NIC entirely. Either way, "plug on, then packet" can silently do nothing. The robust fix is a different firmware setting: set Restore on AC power loss (sometimes called AC Power Recovery or After Power Loss) to Power On, so restoring mains boots the machine on its own and the follow-up packet is a fallback, not the mechanism.

One home-network note on the plug side: a server power supply draws a nontrivial inrush at switch-on. A plug rated for its continuous load is usually fine, but read the rating stamped on the plug before you bet your NAS on it, and do not use a cheap plug for anything whose disk contents matter more than its convenience value. The same preference for local control that carries our post on smart homes that work without the internet applies here: local control paths age better than cloud ones.

Shutting down gracefully, respecting loads

Everything above brings a machine up. The descent is where self-hosted setups get hurt, because there is no magic packet for going down, and because the descent is the moment real work gets interrupted. The principle: graceful shutdown is a negotiation with the workload, and the plug's off switch is not a negotiator.

The command path is boring and reliable: SSH from Home Assistant (the integration docs themselves walk through the SSH suspend recipe, keygen included) and issue the shutdown through a small drain script on the target:

#!/bin/bash
# on the target: replace fast poweroff with a deliberate drain
# 0. stop intake of new work: timers and cron jobs that respect this flag
sudo touch /var/run/draining
while true; do
  # 1. no heavy load is running; int() truncates, so "-lt 2" means a 1-min load under 2.0
  load1=$(awk '{print int($1)}' /proc/loadavg)
  # 2. no local background job flagged
  [ -f /var/run/nightly-job.running ] && sleep 60 && continue
  # 3. media/transcode containers idle: query the service or trust the agent
  # 4. filesystems synced
  sync
  # shut down only when the drain is done
  [ "$load1" -lt 2 ] && sudo systemctl poweroff --no-wall
  sleep 30
done

The point of the loop is not the exact threshold, it is the order of checks: stop intake of new work, let what is running finish, get the filesystems synced, and only then call it idle.

One operational detail: the loop runs until the machine dies, so start it detached on the target — sudo systemd-run --unit=drain /usr/local/bin/drain.sh (or nohup /usr/local/bin/drain.sh >/dev/null 2>&1 &). If Home Assistant starts it over SSH and waits for the call to return, the SSH session will sit open until poweroff and the shell command will time out. The transient unit also leaves you a journal of the drain's last minutes (journalctl -u drain). What counts as load differs by box, and it is worth naming your own version:

  • Media and file servers: active sessions and writes. A transcode or a large copy that dies midstream is the failure this section exists to prevent.
  • Backup targets: the incoming job itself, plus any snapshot or rsync process. Kill this one mid-run and tomorrow you find out how good your restore plan is.
  • Download and sync boxes: the swarm does not pause politely; give the shutdown a grace period where new peers are refused before the process is stopped.
  • Docker hosts: let the engine stop containers with their own stop timeouts, or you trade a clean database shutdown for whatever its crash recovery can manage.

The honest limits

  • Routers and VLANs are a boundary. That is by design (the packet is a broadcast), but it means the sender has to live with the target. If you segment, the place the packet originates is a decision, just like the ones in our smart home VLAN guide; a sender on the wrong side is the most common reason a working-looking setup does not work.
  • Wi-Fi WoL is a lottery. Wireless NICs that support it need the radio powered in a low-power listening state, and all of it depends on firmware, OS, and access-point behavior you do not control. For a server, trust wired; treat Wi-Fi wake as a bonus that may not survive a firmware update.
  • Windows adds its own twist. The NIC's power-management checkbox, the magic packet setting, and Fast Startup all interact: what Windows calls shutdown is often a hibernate (S4), and WoL behavior differs across that line. Check the actual state after every Windows feature update.
  • The wol flag can vanish. Kernel updates, driver changes, and even power events can leave the interface with Wake-on: d again. Arm it via the .link file or the nmcli command from earlier (or your config management), and treat ethtool output as the machine's own assertion.
  • Standby is not zero power. The board, the NIC, and the PSU keep a trickle alive so the magic packet can be heard. That is the entire deal Wake-on-LAN makes, and it is why the smart plug's role (cutting everything when even standby is not wanted) stays valuable.

FAQ

Does Wake-on-LAN work over Wi-Fi?

Sometimes, and never something to plan around. Wireless WoL needs the NIC, its driver, the OS power management, and the access point to cooperate on a low-power listening state. Wired NICs make all of it trivial. For a server, use a cable; treat wireless wake as a documented, unreliable bonus.

Can a Wake-on-LAN packet cross VLANs or the internet?

By default, none will. The magic packet is a broadcast, and routers refuse to forward directed broadcasts — the caveat from earlier cuts both ways: someone who deliberately enables directed broadcasts can make a target reachable from outside, which is why that setting stays off on any WAN-facing interface. If the target lives on another subnet, put the sender on the target's segment (a router, a Pi, Home Assistant on the right interface, or a relay) and send locally.

Should I power a server with a smart plug or with Wake-on-LAN?

Both, for different jobs. WoL handles the clean, frequent operation: waking a machine from standby for its shift, never touching a running OS. The plug owns the extremes: full power-off for long absence, and the hard reset when the OS has stopped answering. Use plug-first, packet-second, and never rely on the plug for regular shutdowns.

How do I turn a machine off from Home Assistant safely?

Give the switch's turn_off an action — a script or shell_command that goes over SSH and issues a real shutdown or suspend, exactly the recipe the integration docs recommend. Never point turn_off at the smart plug: reserve the plug for the hard-reset fallback after wake attempts have failed.

How much power does standby actually use?

A few watts on typical modern hardware, but it is hardware-specific: read the meter on the plug or your PDU rather than trusting a spec sheet. If even a few watts around the clock bothers you, that is the case for the smart plug pairing: cut everything when the machine will be out of service for days.

Further reading

  • Home Assistant Wake on LAN integration (UI button, YAML switch, the template-switch tip, and the SSH suspend recipe): https://www.home-assistant.io/integrations/wakeonlan/
  • Home Assistant Ping integration (a UI-configured binary sensor that tracks whether the machine is really up): https://www.home-assistant.io/integrations/ping/
  • ethtool(8), the WoL flag and its persistence: https://man7.org/linux/man-pages/man8/ethtool.8.html
  • Wake-on-LAN overview, including the magic packet layout and power states: https://en.wikipedia.org/wiki/Wake-on-LAN

What is the first machine in your rack you would stop running 24/7? And what is your escalation ladder when a box stops answering: wake, wait, power-cycle, or something smarter? Drop it in the comments.

Until next time, keep your systems thoughtful.

No comments yet