Automatic sleep and wake up for my local nix builder

Nov 12, 2026

I use an older desktop computer as my Nix builder at home for all my untrusted Nixpkgs work (reviewing PRs, running tests, etc). Since I don't want it powered on and consuming power 24/7, but only run when I want to test builds, here's my quick hack for getting the host to automatically suspend and wake up on demand to build packages.

I use the autosuspend daemon, a program that runs in the background and periodically checks for certain conditions. If a specific state is reached based on those conditions, it automatically suspends the system. Autosuspend supports a wide range of checks to decide if the system should suspend or not.

There's already a NixOS module for autosuspend and I have it configured so that after 5 minutes of not finding an active network connection on the SSH port, the host will suspend. Due to the versatility of NixOS modules, the SSH port is automatically picked up based on the host settings.

Then, on my desktop and laptop, I've got a script that tries to connect to a port on my remote builder host. If it fails, it sends the Wake-On-LAN magic packet to it. This wakes up the builder host in a second or two.

wake-host = pkgs.writeShellApplication {
  name = "wake-host";
  runtimeInputs = with pkgs; [ coreutils netcat wakeonlan ];
  text = ''
    host=$1
    port=$2
    mac=$3

    nc -z -w1 "$host" "$port" && exit 0
    wakeonlan "$mac" >/dev/null
    for _ in $(seq 60); do
      nc -z -w1 "$host" "$port" && exit 0
      sleep 1
    done
    exit 1
  '';
};

Finally, a shell function and an SSH config Match entry tie everything together.

~/.ssh/config

Host builder
  HostName 192.168.1.99
  Port 222

Match originalhost builder exec "wake-host %h %p de:ad:be:ef:13:37"

Shell function

rbuild() {
  nix-build --option restrict-eval true \
    --option allow-import-from-derivation false \
    -I "$(pwd -P)" \
    --eval-store auto --store ssh-ng://builder -A "$1"
}

The shell function uses --eval-store auto so evaluation and instantiation of derivations happen locally, while --store ssh-ng://builder makes the remote builder's Nix store the one where things are built. Opening the SSH connection due to ssh-ng:// is what triggers the wake-host script and wakes up the host with wakeonlan if needed. It's important to note that restrict-eval is set to true, so evals can't access files outside of the current working directory or make network calls.

From the root of my Nixpkgs checkout, once I've reviewed a commit or I've made some changes I want to test, I run rbuild packagename or rbuild nixosTests.nameoftest. The expressions get evaluated locally and the derivations are transferred to the builder over SSH. The builder, then, downloads the source code of the target packages and builds them or runs the NixOS test as requested. The build outputs stay in the builder's store (which will be garbage-collected automatically later) and never touch my local store. Five minutes after the build has finished and the SSH connection has closed, the builder host goes back to sleep.

This setup isn't without issues. If the SSH connection drops, say because I closed the lid of my laptop, the build will be canceled on the remote builder. In an ideal setup, I would schedule builds in a proper build system (maybe nix-grpc-store?) and have builder hosts wake up and pick up builds on their own. But hey, it's a hack, it works and hacks are fun :).

Tags: nix nixos


Comments on Fediverse.