# Trugamr/wol: Wake-On-LAN from the CLI, the browser and a cron schedule

> A small Go tool that sends Wake-On-LAN magic packets, keeps a named machine list in YAML, and adds a web interface plus scheduled wake-ups. It fits a homelab with a handful of hosts, not a fleet-management platform.

**Trugamr/wol** — 🦭 Wake up your devices with a single command or click. A Wake-On-LAN tool that works via CLI and web interface.

- Repository: https://github.com/Trugamr/wol
- Stars: 700 · Forks: 25
- Language: Go
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/trugamr-wol

## The problem wol solves, and the homelab it assumes

A machine that is powered off still has a network card listening. Wake-On-LAN exploits that: the card watches for a magic packet, a frame containing the target MAC address repeated, and powers the host up when it sees one. The awkward part is never the packet itself. It is remembering MAC addresses, remembering which subnet a device sits on, and having a way to fire the packet from a phone or a browser when the desktop you would normally send it from is the machine that is asleep.

Trugamr/wol is a Go program that covers that gap. You describe your machines once in a YAML file under a machines list, each entry with a name and a mac, and optionally an ip used for status checking. From there the same config feeds three surfaces: a CLI (wol list, wol send), a web interface started by wol serve on port 7777, and an optional schedules block that wakes machines on a cron expression. The audience is a homelab or small office where the machine count is in the single digits or low tens and the person running it is also the person who wrote the config. There is no agent on the target, no discovery protocol, and no inventory database. If a device's MAC address is unknown to you, wol cannot help you find it.

## Inside the binary: Koanf config, pro-bing status, robfig/cron schedules

The dependency list in go.mod is a fair map of the design. Configuration is read through knadh/koanf with the YAML, file, rawbytes and structs providers, which is how the same schema arrives either from a file on disk or from the WOL_CONFIG environment variable holding raw YAML. Status checking uses prometheus-community/pro-bing, which is why the config has a ping block with a privileged flag: unprivileged ICMP is the default, and setting ping.privileged to true switches to privileged pings when the host requires them. Scheduling is robfig/cron/v3, and the CLI is spf13/cobra, which is where the list, send, serve and version subcommands come from. Packet construction lives in its own magicpacket/ directory rather than being inlined into the command layer.

The data flow for a single wake is short. A command or an HTTP request resolves a machine name to a MAC address, the broadcast target is resolved by precedence (CLI flag, then per-machine config, then global config, then the built-in 255.255.255.255:9), and a magic packet is written to a UDP socket. Because the packet is a broadcast, the operating system chooses the outgoing interface, and that choice is the source of most failures. The README is explicit about this: on a host with more than one interface, such as WSL2 or a machine carrying Docker bridges, the packet may leave through the wrong one and the target never sees it. The remedy the README gives is to set the broadcast address of the target's own subnet, for example 192.168.1.255 for a device at 192.168.1.100/24, so routing picks the correct interface. Per-machine broadcast blocks exist for the case where machines live on different VLANs and unset fields fall back to the global value.

Schedules are validated when serve starts. A schedule naming a machine that does not exist, or carrying a cron expression robfig/cron rejects, makes the process exit with an error instead of running and silently never firing. That is the right default for a tool whose whole job is unattended wake-ups, and it is worth knowing before you deploy, because a typo in a schedule name takes the web interface down with it.

## Installing wol and waking a first machine

The README lists three install paths. Pre-built binaries cover Linux x86_64, arm64 and armv7, macOS x86_64 and arm64, and Windows x86_64, published on the releases page. If you have a Go toolchain, the module installs directly:

```bash
go install github.com/trugamr/wol@latest
```

That places a wol binary in your Go bin directory, which is the quickest way to try a single send before committing to a config file. You can wake a device by MAC address with no configuration at all:

```bash
wol send --mac "00:11:22:33:44:55"
```

The README does not state what the command prints on success, so treat the absence of an error as the signal and check the target. To make names usable, create config.yaml. The search order is the current directory, then ~/.wol/config.yaml, then /etc/wol/config.yaml:

```yaml
machines:
  - name: desktop
    mac: "00:11:22:33:44:55"
    ip: "192.168.1.100"
  - name: server
    mac: "AA:BB:CC:DD:EE:FF"

server:
  listen: ":7777"
```

With that file in place, wol list shows the names it parsed and wol send --name desktop sends the packet. Passing an explicit file is authoritative: wol serve --config /etc/wol/config.yaml must find that file or the process exits, and the default search locations plus WOL_CONFIG are ignored. On startup, serve logs which config source it loaded, which is the first thing to read when a machine you added does not appear in the browser page.

The container route is the one to think about before copying. The README's docker run uses --network host and mounts the config at /etc/wol/config.yaml, and it notes that host networking is recommended for the packets to work on your local network:

```bash
docker run --network host -v $(pwd)/config.yaml:/etc/wol/config.yaml ghcr.io/trugamr/wol:latest
```

That command runs the default command, not serve. To get the web interface you add serve after the image name, as the docker-compose examples do. Image tags are latest and X.Y.Z for named releases, with edge tracking the latest commit on main for unreleased changes. Note the trade-off in the compose file: the environment-variable method puts the entire YAML document inside WOL_CONFIG, which works but moves your machine list into a compose file rather than a file you can edit on its own.

## Where wol stops being the right tool

The most common failure is not in wol at all. A magic packet only works if the target's firmware has wake-on-magic-packet enabled and the NIC keeps receiving power in the shutdown state. On many boards that is off by default, and on some it is disabled again by a fast-startup setting in the operating system. wol will report no error in that situation, because sending a UDP broadcast succeeds regardless of whether anything is listening. Debugging therefore starts at the target's BIOS or UEFI, not in the YAML.

The second boundary is the network. Wake-On-LAN is a layer-2 broadcast, so it does not cross a router without a relay or a directed broadcast configuration. wol's per-machine broadcast override helps when the sending host has multiple interfaces or when machines sit on different subnets reachable from that host, but it does not turn the tool into a remote wake service. If you want to wake a machine from outside your home network, you are placing the web interface behind something, and the README points at examples/reverse-proxy.yml for a reverse proxy with basic auth and https rather than describing authentication inside wol itself. Nothing in the README suggests the serve command has its own user accounts or access control, so the web interface should be treated as a LAN-only surface unless you front it.

Scale is the third limit. The config model is a flat machines list with optional schedules, and there is no grouping, tagging, role-based access or audit trail described. A homelab of five machines is comfortable. A building with fifty hosts and several operators would be fighting the file, and a tool that stores state in a database would fit better. Finally, wol is a waker, not a power manager: the README documents status checking through ping and wake-ups on a schedule, and nothing about shutdown, reboot or graceful power control.

## wol against UpSnap and a hand-rolled script

The related searches around this project include UpSnap, and the comparison is informative because the two tools answer different questions. UpSnap is a self-hosted web application with a database and a UI as the primary interface, commonly run in a container on a NAS. Its centre of gravity is the browser page and stored state. wol inverts that: the YAML file is the source of truth, the CLI is a first-class way to use it, and the web interface is one of two front ends over the same config. If your workflow is a shell script, a cron job on another host, or an Ansible task that calls a binary, wol's model fits without an API layer. If your workflow is several people clicking buttons and expecting saved history, a database-backed application is the better shape.

The other alternative is the script you would otherwise write yourself. A magic packet is small enough that a few lines of bash or Python can produce one, and plenty of people do exactly that. What a script does not give you is a validated config schema, a named machine list shared by a CLI and a web page, per-machine broadcast overrides with defined precedence, or a scheduler that refuses to start on a bad cron expression. Those are the parts of wol that are tedious to reimplement correctly. The parts that are easy to reimplement, namely building the packet and sending it, are also the parts you can debug in an afternoon.

## Maintenance, licensing and what an upgrade costs

The repository is not archived, and the last push was on 2026-06-14. The release history shows v0.1.0 in May 2025, then v0.2.0 and v0.3.0 on consecutive days in June 2026, which reads as a burst of work rather than a steady cadence. Version numbers below 1.0 mean the config schema is not promised to be stable, and the schedules block is recent enough to be part of that risk. The practical cost of an upgrade is small in normal cases: replace the binary or pull a new image tag, and check that serve still starts, because startup is where config and cron validation happen. Pinning X.Y.Z in a compose file avoids surprises, at the price of missing fixes. The edge tag tracks main and should be treated as unreleased.

Licensing is MIT, which is permissive and imposes few obligations beyond keeping the copyright notice and permission text with copies or substantial portions. That matters if you vendor the magicpacket/ package into another Go program. The dependencies carry their own licences, and the notable ones here are cobra, koanf, robfig/cron and pro-bing, all of which are widely used in Go projects. This is a description of the licence text, not legal advice; if you redistribute wol inside a product, have someone check the notice requirements rather than taking this paragraph as clearance.

## Conclusion

Adopt wol if you keep a short list of machines with known MAC addresses and want one config.yaml that both a CLI and a browser page read, with cron schedules validated at startup. Skip it if you need out-of-band access from outside your LAN without a reverse proxy, or if you expect the web interface to authenticate users on its own: the README points to examples/reverse-proxy.yml for basic auth. Before rolling it out, confirm that your target NICs have wake-on-magic-packet enabled in firmware, then run wol list and a single wol send --name <machine> while watching the serve log to see which config source loaded and whether the packet left the right interface.

## FAQ

### What is a Wake-On-LAN magic packet, and how does Trugamr/wol send one?

It is a network frame that carries the target machine's MAC address and is watched for by a powered-down network card, which then starts the host. wol builds the packet in its magicpacket package and sends it as a UDP broadcast, by default to 255.255.255.255 on port 9.

### Should wake on magic packet be enabled for wol to work?

Yes. The packet only does anything if the target's firmware has wake-on-magic-packet enabled and the network card keeps receiving power while the machine is off. wol reports no error when it sends to a machine that is not configured to listen, so check the target's BIOS or UEFI settings first.

### How do I trigger a wake-up with Trugamr/wol?

From the CLI with wol send --name <machine> for a configured machine, or wol send --mac "00:11:22:33:44:55" for an address that is not in the config. Alternatively run wol serve and use the one-click wake buttons in the web interface on port 7777.

### Can I send a WoL packet from the command line with Trugamr/wol?

Yes, the CLI is the primary interface. wol list shows configured machines, wol send wakes one by name or MAC address, and --broadcast and --port override the destination for a single send when a device sits on a specific subnet.

## Sources

- [Issues](https://github.com/Trugamr/wol/issues)
- [License: MIT](https://github.com/Trugamr/wol/blob/main/LICENSE)
- [README](https://github.com/Trugamr/wol/blob/main/README.md)
- [Releases](https://github.com/Trugamr/wol/releases)
- [Trugamr/wol on GitHub](https://github.com/Trugamr/wol)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/trugamr-wol
