Squashed commit of the following:
commit bf4873eb012235b4d82cd79c4f508064ead301ab Author: Joao Porta <jpedro.porta@gmail.com> Date: Fri Jul 17 17:24:26 2026 -0300 wake on lan
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
../../modules/nixos/steam
|
../../modules/nixos/steam
|
||||||
../../modules/nixos/awsvpn
|
../../modules/nixos/awsvpn
|
||||||
../../modules/nixos/tailscale
|
../../modules/nixos/tailscale
|
||||||
|
../../modules/nixos/wake-on-lan
|
||||||
];
|
];
|
||||||
|
|
||||||
# Bootloader
|
# Bootloader
|
||||||
@@ -115,6 +116,7 @@
|
|||||||
hyprland.enable = true;
|
hyprland.enable = true;
|
||||||
steam.enable = true;
|
steam.enable = true;
|
||||||
tailscale.enable = true;
|
tailscale.enable = true;
|
||||||
|
wake-on-lan.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
##------------------------------
|
##------------------------------
|
||||||
|
|||||||
@@ -20,36 +20,31 @@
|
|||||||
size = 10.3;
|
size = 10.3;
|
||||||
normal = {
|
normal = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "Regular";
|
||||||
};
|
};
|
||||||
bold = {
|
bold = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "ExtraBold";
|
||||||
};
|
};
|
||||||
italic = {
|
italic = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "Oblique";
|
||||||
};
|
};
|
||||||
bold_italic = {
|
bold_italic = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "ExtraBold Oblique";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
window = {
|
window = {
|
||||||
decorations = "None";
|
decorations = "None";
|
||||||
opacity = 0.8;
|
opacity = 0.8;
|
||||||
blur = true;
|
blur = false;
|
||||||
dynamic_padding = true;
|
dynamic_padding = true;
|
||||||
padding = {
|
padding = {
|
||||||
x = 10;
|
x = 10;
|
||||||
y = 16;
|
y = 16;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
keyboard = {
|
|
||||||
bindings = [
|
|
||||||
{
|
|
||||||
key = "Return";
|
|
||||||
mods = "Shift";
|
|
||||||
chars = "\u001B\r";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.custom.wake-on-lan;
|
||||||
|
|
||||||
|
iface = "enp14s0";
|
||||||
|
|
||||||
|
mac = "d8:43:ae:5a:ae:12";
|
||||||
|
|
||||||
|
wake-jpporta-nixos = pkgs.writeShellScriptBin "wake-jpporta-nixos" ''
|
||||||
|
exec ${pkgs.wakeonlan}/bin/wakeonlan -i "''${1:-255.255.255.255}" ${mac}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.custom.wake-on-lan = {
|
||||||
|
enable = lib.mkEnableOption "Wake-on-LAN on the wired NIC (magic-packet mode)";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
# Keep `wol g` applied on the NIC after the link is up (boot path).
|
||||||
|
systemd.services."wol-${iface}" = {
|
||||||
|
description = "Enable Wake-on-LAN (magic packet) on ${iface}";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
wants = [
|
||||||
|
"network-online.target"
|
||||||
|
"NetworkManager.service"
|
||||||
|
];
|
||||||
|
after = [
|
||||||
|
"network-online.target"
|
||||||
|
"NetworkManager.service"
|
||||||
|
"NetworkManager-wait-online.service"
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = "${pkgs.ethtool}/bin/ethtool -s ${iface} wol g";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Re-apply `wol g` after resume from suspend / hibernate so NetworkManager
|
||||||
|
# cannot silently drop the WOL flag on link renegotiation.
|
||||||
|
systemd.services."wol-${iface}-resume" = {
|
||||||
|
description = "Re-apply Wake-on-LAN (magic packet) on ${iface} after resume";
|
||||||
|
wantedBy = [
|
||||||
|
"suspend.target"
|
||||||
|
"hibernate.target"
|
||||||
|
"hybrid-sleep.target"
|
||||||
|
];
|
||||||
|
after = [
|
||||||
|
"systemd-suspend.service"
|
||||||
|
"systemd-hibernate.service"
|
||||||
|
"systemd-hybrid-sleep.service"
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${pkgs.ethtool}/bin/ethtool -s ${iface} wol g";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.ethtool
|
||||||
|
pkgs.wakeonlan
|
||||||
|
wake-jpporta-nixos
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-07-17
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
`jpporta-nixos` is a desktop machine with a wired Ethernet interface (`enp14s0`, MAC `d8:43:ae:5a:ae:12`) managed by NetworkManager. The user wants to power the machine on remotely via Wake-on-LAN so it can be reached over Tailscale + SSH without manual intervention.
|
||||||
|
|
||||||
|
WOL requires three layers of configuration:
|
||||||
|
|
||||||
|
1. **Firmware (BIOS/UEFI):** "Wake on LAN" must be enabled on the NIC, and the machine must remain in a low-power state after shutdown (most desktop boards default to this; some servers default to "power on after AC loss" which would defeat the purpose).
|
||||||
|
2. **Kernel/driver (NixOS):** The NIC driver must be told to keep listening for magic packets while the system is off. For most Intel NICs this means `ethtool -s <iface> wol g` (magic packet mode).
|
||||||
|
3. **Network:** Magic packets are sent to the layer-2 broadcast address (`ff:ff:ff:ff:ff:ff`) and rely on the NIC matching its own MAC. The host cannot route or filter them above the NIC, so no userspace daemon is required to *receive* a packet. A userspace tool (`wakeonlan`) is required to *send* one.
|
||||||
|
|
||||||
|
NixOS already provides `networking.wakeOnLan.enable`, but that option is hard-coded to `eth0` and is incompatible with predictable interface names (`enp14s0`). The NixOS-supported path for a named, predictable interface is a custom systemd unit that invokes `ethtool` against the specific interface. This is the approach used here because it survives reboots, resumes, and NetworkManager reconnects.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- Persist `wol g` on `enp14s0` across reboots and resume-from-suspend.
|
||||||
|
- Provide `ethtool` and `wakeonlan` on `$PATH` so the user can inspect and send magic packets.
|
||||||
|
- Provide a convenience script `wake-jpporta-nixos` that sends a WOL packet to this machine's MAC.
|
||||||
|
- Wrap the configuration in a reusable module (`custom.wake-on-lan.enable`) consistent with existing patterns.
|
||||||
|
- Document the out-of-band BIOS / router configuration steps.
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
- Setting BIOS options (firmware is out of reach from NixOS).
|
||||||
|
- Configuring router port-forwarding for WOL-over-internet (router is out of scope).
|
||||||
|
- Listening for / relaying WOL packets on this host — that is the NIC's job once `wol g` is set.
|
||||||
|
- Changing the network interface naming scheme or replacing NetworkManager.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. **Per-interface systemd unit over `networking.wakeOnLan.enable`.**
|
||||||
|
- The top-level `networking.wakeOnLan.enable` is hard-coded to `eth0`, which doesn't match the predictable interface name `enp14s0` used here.
|
||||||
|
- Alternative considered: `networking.interfaces.enp14s0.wakeOnLan.enable = true`. This option does exist in modern NixOS, but on this machine it interacts awkwardly with NetworkManager (which re-loads NIC settings on reconnect) and the underlying implementation still relies on an ethtool invocation that may be racy with NetworkManager bringing the link up.
|
||||||
|
- **Decision:** Use a dedicated systemd `oneshot` service with a `network-online.target` dependency. This makes the WOL setting idempotent, runs after NetworkManager is up, and can also be triggered on resume-from-suspend by adding it to the `suspend.target` resume path. Concretely we ship a unit `wol-enp14s0.service` that runs `ethtool -s enp14s0 wol g`.
|
||||||
|
|
||||||
|
2. **Package set: `ethtool` + `iputils` (which provides `wakeonlan`).**
|
||||||
|
- `pkgs.ethtool` is the standard tool for inspecting and setting NIC driver options, including WOL modes.
|
||||||
|
- `pkgs.iputils` provides `wakeonlan`, the simplest CLI for sending a magic packet to a target MAC.
|
||||||
|
- Alternative considered: `pkgs.wol` (the separate, older `wol` program). `wakeonlan` from iputils is more widely used, has a simpler interface (`wakeonlan <mac>`), and is what most WOL guides assume.
|
||||||
|
|
||||||
|
3. **Convenience wrapper script `wake-jpporta-nixos`.**
|
||||||
|
- Hard-codes the MAC of this machine's wired NIC (`d8:43:ae:5a:ae:12`) into a small `writeShellScriptBin`-packaged script.
|
||||||
|
- Rationale: avoids having to remember or look up the MAC every time. The wrapper accepts an optional broadcast IP argument so the user can target `192.168.0.255` from off-LAN once the router is configured to forward UDP 9 to that broadcast.
|
||||||
|
|
||||||
|
4. **Module pattern: `modules/nixos/wake-on-lan/default.nix` with `custom.wake-on-lan.enable`.**
|
||||||
|
- Mirrors the existing pattern used by `modules/nixos/tailscale/` and `modules/nixos/hyprland/`.
|
||||||
|
- Defaults to disabled. Host (`jpporta-nixos`) opts in via `custom.wake-on-lan.enable = true`.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- **[Risk]** `ethtool -s ... wol g` only takes effect while the kernel NIC driver is loaded. Some BIOS/firmware combinations fully power down the NIC on shutdown, making WOL impossible regardless of the OS setting.
|
||||||
|
- **Mitigation:** Document this clearly in the change notes. The user must verify with `ethtool enp14s0` after a `systemctl poweroff` that `Supports Wake-on: pumbg` and `Wake-on: g` are still present.
|
||||||
|
|
||||||
|
- **[Risk]** Some desktop boards default to "Power on after AC loss = Power on" which would make the machine boot after any power outage regardless of WOL — undermining the use case.
|
||||||
|
- **Mitigation:** Document the BIOS setting ("After Power Loss" → "Power Off" or "Stay Off") in the change notes.
|
||||||
|
|
||||||
|
- **[Risk]** NetworkManager may re-apply link settings on resume, overwriting `wol g`.
|
||||||
|
- **Mitigation:** The systemd unit is wired to run on resume via a `path`/`service` pair (or a manual `systemctl restart wol-enp14s0.service` hook). For the initial implementation we trigger it on boot and rely on the user to re-run if it ever drifts; the follow-up task in `tasks.md` adds the resume hook.
|
||||||
|
|
||||||
|
- **[Trade-off]** Hard-coding the MAC and interface name in the Nix module means the module is specific to `jpporta-nixos`. We accept this trade-off because the module is only enabled on this host and the alternative (parameterizing) would add ceremony for a single user.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
The user wants to power on `jpporta-nixos` remotely via Wake-on-LAN (WOL). This is useful when the machine is shut down but the user wants to access it from another device (e.g., over Tailscale + SSH) without manually pressing the power button. Currently the machine has no WOL configuration: nothing keeps the NIC in a low-power listening state, and no tooling exists on the host to inspect or send WOL packets.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Enable Wake-on-LAN on the wired Ethernet interface (`enp14s0`) so the NIC keeps listening for magic packets while the system is powered off.
|
||||||
|
- Add `ethtool` so the user can inspect the current WOL settings and supported modes on the NIC.
|
||||||
|
- Add `wol` (the `iputils`-style `wakeonlan` command) so WOL packets can be sent from this host to other machines, and from other machines back to this one (using its MAC `d8:43:ae:5a:ae:12`).
|
||||||
|
- Add a small helper script (`wake-jpporta-nixos`) that sends a WOL magic packet to this machine's MAC address, so it can be invoked by name.
|
||||||
|
- Document the manual BIOS-level steps that are required for WOL to work (these cannot be set from NixOS).
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
- `wake-on-lan`: Configures Wake-on-LAN on the host's wired NIC and provides tooling/scripts to inspect and send magic packets.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
- None
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- **Code:** A new optional module `modules/nixos/wake-on-lan/` will be added and imported into `hosts/jpporta-nixos/configuration.nix`. The module will expose a `custom.wake-on-lan.enable` toggle consistent with the project's existing module pattern (see `modules/nixos/tailscale/`).
|
||||||
|
- **System:** A systemd unit will be installed that runs `ethtool -s enp14s0 wol g` on boot and on resume, persisting the WOL setting across reboots and sleep states.
|
||||||
|
- **User:** Two new commands will be on `$PATH`: `ethtool` and `wakeonlan`, plus a convenience wrapper `wake-jpporta-nixos`.
|
||||||
|
- **Hardware:** Requires BIOS-level "Wake on LAN" to be enabled on the motherboard and a stable AC power source. The user must configure these out-of-band.
|
||||||
|
- **Network:** To wake the machine from outside the LAN, the router must forward UDP port 9 (or 7) to the broadcast address. This is a router-side concern, not configurable from NixOS.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Persistent Wake-on-LAN on the wired NIC
|
||||||
|
The system SHALL keep the wired Ethernet interface (`enp14s0`) configured to wake on magic-packet while powered off.
|
||||||
|
|
||||||
|
#### Scenario: WOL applied on boot
|
||||||
|
- **WHEN** the system finishes booting
|
||||||
|
- **THEN** `ethtool enp14s0` SHALL report `Wake-on: g`
|
||||||
|
|
||||||
|
#### Scenario: WOL applied on resume from suspend
|
||||||
|
- **WHEN** the system resumes from suspend
|
||||||
|
- **THEN** `ethtool enp14s0` SHALL report `Wake-on: g`
|
||||||
|
|
||||||
|
### Requirement: Wake-on-LAN tooling available
|
||||||
|
The system SHALL provide `ethtool` and `wakeonlan` on `$PATH` so the user can inspect and trigger WOL.
|
||||||
|
|
||||||
|
#### Scenario: Inspect WOL state
|
||||||
|
- **WHEN** the user runs `ethtool enp14s0`
|
||||||
|
- **THEN** the command SHALL exit 0
|
||||||
|
- **AND** the output SHALL include `Supports Wake-on:` and `Wake-on:` lines
|
||||||
|
|
||||||
|
#### Scenario: Send a magic packet
|
||||||
|
- **WHEN** the user runs `wakeonlan <MAC>` (or `wake-jpporta-nixos`)
|
||||||
|
- **THEN** the command SHALL exit 0
|
||||||
|
- **AND** a UDP magic-packet SHALL be broadcast to `255.255.255.255:9`
|
||||||
|
|
||||||
|
### Requirement: Convenience wrapper to wake this host
|
||||||
|
The system SHALL provide a `wake-jpporta-nixos` command that sends a WOL magic packet to this machine's wired NIC MAC.
|
||||||
|
|
||||||
|
#### Scenario: Wake this machine from LAN
|
||||||
|
- **WHEN** the user runs `wake-jpporta-nixos`
|
||||||
|
- **THEN** the host's NIC SHALL receive the magic packet
|
||||||
|
- **AND** the system SHALL begin booting
|
||||||
|
|
||||||
|
#### Scenario: Wake this machine through a custom broadcast address
|
||||||
|
- **WHEN** the user runs `wake-jpporta-nixos 192.168.0.255`
|
||||||
|
- **THEN** the magic packet SHALL be sent to the `192.168.0.255` broadcast address
|
||||||
|
- **AND** the host SHALL begin booting
|
||||||
|
|
||||||
|
### Requirement: Reusable opt-in module
|
||||||
|
The system SHALL expose a `custom.wake-on-lan.enable` option that, when enabled, configures all of the above.
|
||||||
|
|
||||||
|
#### Scenario: Module disabled
|
||||||
|
- **WHEN** `custom.wake-on-lan.enable` is `false` or unset
|
||||||
|
- **THEN** the system SHALL NOT install the WOL systemd unit
|
||||||
|
- **AND** the system SHALL NOT install the `ethtool`, `wakeonlan`, or `wake-jpporta-nixos` packages
|
||||||
|
|
||||||
|
#### Scenario: Module enabled
|
||||||
|
- **WHEN** `custom.wake-on-lan.enable` is `true`
|
||||||
|
- **THEN** the WOL systemd unit SHALL be active
|
||||||
|
- **AND** the `wake-jpporta-nixos` command SHALL be on `$PATH`
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
## 1. Create the WOL NixOS module
|
||||||
|
|
||||||
|
- [x] 1.1 Create `modules/nixos/wake-on-lan/default.nix` with a `custom.wake-on-lan.enable` option.
|
||||||
|
- [x] 1.2 Define a systemd `oneshot` service `wol-enp14s0.service` that runs `ethtool -s enp14s0 wol g`.
|
||||||
|
- [x] 1.3 Wire the unit's `after`/`wants` to `network-online.target` and `NetworkManager.service` so it runs after the link is up.
|
||||||
|
- [x] 1.4 Add the unit to the resume path so `wol g` is re-applied after suspend/resume (use `systemd-suspend.service` or a `path` unit hook).
|
||||||
|
|
||||||
|
## 2. Add packages and the convenience wrapper
|
||||||
|
|
||||||
|
- [x] 2.1 Add `pkgs.ethtool` and `pkgs.wakeonlan` to `environment.systemPackages` when the module is enabled. *(Note: `pkgs.iputils` does not provide `wakeonlan` in nixpkgs — see implementation note.)*
|
||||||
|
- [x] 2.2 Create a `wake-jpporta-nixos` shell script via `pkgs.writeShellScriptBin` that invokes `wakeonlan -i ${1:-255.255.255.255} d8:43:ae:5a:ae:12`.
|
||||||
|
- [x] 2.3 Add the wrapper to `environment.systemPackages`.
|
||||||
|
|
||||||
|
## 3. Wire the module into the host
|
||||||
|
|
||||||
|
- [x] 3.1 Add `../../modules/nixos/wake-on-lan` to `imports` in `hosts/jpporta-nixos/configuration.nix`.
|
||||||
|
- [x] 3.2 Add `custom.wake-on-lan.enable = true;` under the existing `custom = { ... };` block in the host config.
|
||||||
|
|
||||||
|
## 4. Verify and document
|
||||||
|
|
||||||
|
- [x] 4.1 Rebuild the system with `sudo nixos-rebuild switch --flake .#jpporta-nixos`.
|
||||||
|
- [x] 4.2 Run `ethtool enp14s0` and confirm `Supports Wake-on:` lists `g` and `Wake-on: g`.
|
||||||
|
- [x] 4.3 Run `systemctl status wol-enp14s0.service` and confirm the unit is `active (exited)`.
|
||||||
|
- [x] 4.4 Run `wake-jpporta-nixos` once while the machine is up to confirm the script runs without error (it will be a no-op when the host is already up).
|
||||||
|
- [x] 4.5 Shut the machine down, then run `wake-jpporta-nixos` from another machine on the same LAN and confirm `jpporta-nixos` powers on.
|
||||||
|
- [x] 4.6 Document the BIOS-level prerequisite ("Wake on LAN" enabled, "After Power Loss" → Power Off / Stay Off) in the change notes or a README so it's not lost.
|
||||||
Reference in New Issue
Block a user