Build and Deploy
Table of Contents
1. Build commands
cd ~/dotfiles nix flake check # lint the whole flake nix build .#nixosConfigurations.<host>.config.system.build.toplevel # build without root sudo nixos-rebuild switch --flake .#<host> # apply
The first two need no root and no target machine. Run them before touching anything.
1.1. Building as a user, activating as root
On a host where the flake checkout lives in a user's home, sudo nixos-rebuild
can fail to read the repository — the version-control library refuses a
repository owned by another user. Splitting the two halves works:
nix build .#nixosConfigurations.<host>.config.system.build.toplevel sudo nix-env -p /nix/var/nix/profiles/system --set ./result sudo ./result/bin/switch-to-configuration switch
1.2. Proving a change is a no-op
For a refactor that should change nothing, compare derivation paths. This is pure evaluation — no building, no root, and it works for hosts other than the one you are sitting at:
nix eval --raw .#nixosConfigurations.<host>.config.system.build.toplevel.drvPath
Identical before and after means provably inert. This is the right tool for namespace renames, module moves and formatting sweeps.
1.3. Do not build other hosts' closures carelessly
Building a graphical host's toplevel on a small or virtualised machine can
source-compile large packages that are not in a binary cache, exhaust memory and
take the environment down with it. Use the drvPath comparison above when all
you need is a comparison.
2. Updating
NixOS has no apt upgrade. Updates are driven by moving the flake's input pins.
nix flake update # bump ALL inputs sudo nixos-rebuild switch --flake .#<host>
Everything pinned by the flake moves together — the language toolchains, the editor and its packages, the compositor. A new generation is created, so the blast radius of a bad update is one rollback.
To move a single input:
nix flake lock --update-input <input> sudo nixos-rebuild switch --flake .#<host>
3. Rolling back
sudo nixos-rebuild switch --rollback
Every rebuild creates a generation and every generation is a target. The bootloader also lists them, which is the escape hatch when a change breaks the graphical session and you cannot get a shell.
3.1. What rollback does not cover
Rollback restores the system closure. It does not restore anything the system does not own:
- Files symlinked out of the store for live editing are not
generation-pinned — a rollback leaves them at whatever the working tree holds.
See
emanix.src.liveElispin Options. - Data is data. Rollback is not a backup.
- Anything a service mutated at runtime — a database, a cache — stays mutated.
4. Garbage collection
Collection runs daily, deleting generations older than two weeks, and the store is auto-optimised.
There is also a mid-build emergency valve: when free space falls below a floor, Nix collects during the build until a ceiling is available again. That valve is the real safety net — a timer only helps between builds, and it is during a large build that a machine actually runs out of room.
Manual collection, when needed:
sudo nix-collect-garbage -d # delete old generations, then collect nix store gc # collect only what nothing references
4.1. On a virtualised host
A virtual disk image is a high-water mark: it grows and does not shrink when files are deleted inside the guest. Collecting garbage frees space to the guest but not to the host, so image growth needs a separate, deliberate compaction step. Plan for it rather than being surprised by it.
5. Formatting and linting
nix fmt # format the tree nix develop # shell with the language server and linters
The dev shell carries a Nix language server, the formatter, a dead-code finder and a linter — the same tools CI would run, available before you commit.