Rebuild modes and generations

Updated

July 30, 2026

Rebuild modes and generations

Goal: Master nixos-rebuild modes, system generations, and rollback—including a deliberate break and recovery on the disposable lab.

Why this chapter matters

NixOS’s famous safety is not automatic—it is generational profiles + boot entries + knowing which rebuild verb you used. Without that theory, “rollback” is folklore.

Ops motivation: production incidents need a rehearsed rollback card. This chapter builds that muscle on a disposable VM so production is not your first practice.


Theory 1 — System profile and generations

NixOS points the running system at a profile:

/nix/var/nix/profiles/system → system-N-link → store path

Each successful deployment can add a generation N. Boot loaders list generations so you can boot an older one when the new world is wrong.

Concept Meaning
Generation A complete system closure you can activate/boot
Current What the profile points at now
Rollback Point profile (and/or boot default) at older generation

Inspect

sudo nix-env -p /nix/var/nix/profiles/system --list-generations
readlink -f /nix/var/nix/profiles/system
ls -l /nix/var/nix/profiles/system*

Theory 2 — Rebuild modes

sudo nixos-rebuild <mode> --flake .#lab
Mode Effect
switch Build + activate now + set boot default to new gen
boot Build + set boot default; do not activate until reboot
test Build + activate now; do not change boot default
build Build only; no activation
dry-activate Show activation plan (when supported/useful)
build-vm Build a VM of the config (extra workflows)

Choosing modes

Situation Prefer
Normal daily change switch
Risky change; want easy reboot escape test first or boot
Remote SSH-only host Extra care—bad switch can lock you out
CI / check build
Kernel/boot experiments boot then reboot deliberately

Remote note

If you break networking/SSH on a remote machine with switch, you may need console. Lab VMs have consoles—use them.


Theory 3 — Activation vs boot default

test:   running config changes; reboot may return to previous default
switch: running + default updated
boot:   default updated; running stays old until reboot

Worked story

  1. test a firewall change → SSH still works →
  2. switch to commit it to boot default

Or:

  1. boot a kernel change → reboot when ready →
  2. If unbootable, select previous generation in boot menu

Decision flowchart

Is the change network/SSH sensitive?
  yes → prefer test (with console available) → switch
Is the change kernel/bootloader?
  yes → consider boot → reboot → verify
Otherwise → switch
Want no activation? → build

Theory 4 — Listing and switching generations

sudo nix-env -p /nix/var/nix/profiles/system --list-generations
# or
sudo nixos-rebuild list-generations  # when available in your version

Rollback helpers:

sudo nixos-rebuild switch --rollback
# or boot older generation from boot menu

Also:

sudo /nix/var/nix/profiles/system-<N>-link/bin/switch-to-configuration switch

Prefer documented nixos-rebuild interfaces when possible.

Boot menu recovery

When userland is too broken to roll back from a shell:

  1. Reboot
  2. Open boot loader menu (systemd-boot/GRUB key)
  3. Select previous generation
  4. Fix flake; switch a good config

Document your hypervisor key chords in the rollback card.


Theory 5 — What is not rolled back

Rolled back with system gen Not rolled back
Packages, units, users declared in config Your data in /home, /var
Most /etc files managed by NixOS Databases, untracked files
Kernel (for that gen) Out-of-band disk edits
Firewall rules from config Manual live nft edits as SoT (don’t)

Punchline: rollback restores system closure, not time-travel for application data.


Theory 6 — GC and generations

Old generations root large closures. Cleaning:

sudo nix-collect-garbage -d   # deletes old profile gens — careful
sudo nixos-rebuild …          # after understanding
Important

Do not delete generations until you have a known good current system and understand you are dropping rollback points.

Action Risk
Keep last N gens Disk vs safety trade
GC after known-good deploy Frees space
GC before verifying Removes escape hatches

Theory 7 — Flake + rebuild flags worth knowing

sudo nixos-rebuild switch --flake .#lab
sudo nixos-rebuild switch --flake .#lab --show-trace
sudo nixos-rebuild switch --flake .#lab -L
sudo nixos-rebuild switch --flake path:/home/you/nixos-config#lab
Flag Use
--show-trace Eval errors
-L Build logs
--impure Avoid unless you know why
--upgrade / channel habits Prefer flake lock updates instead

Lock updates are a separate concern (Home and flake layout part).


Theory 8 — Activation scripts and failure modes

Activation runs scripts that update bootloader entries, restart units, rewrite /etc, etc.

Failure class Response
Eval No new gen; fix Nix
Build No activation; fix package
Activation partial Read output; may need rollback/console
Unit failed systemctl, journal; gen may still be current

test is valuable because reboot can abandon a bad experiment if boot default never moved.


Theory 9 — Naming generations in ops notes

Track in docs/rollback.md:

Field Example
Last known good gen 12
Last change summary “opened ssh + htop”
Snapshot name pre-firewall

Generations numbers alone are not memory—annotate them.


Worked examples bank

Example A — Safe build

cd ~/nixos-config
nixos-rebuild build --flake .#lab

Example B — test then switch

sudo nixos-rebuild test --flake .#lab
# exercise the change
sudo nixos-rebuild switch --flake .#lab

Example C — rollback

sudo nixos-rebuild switch --rollback

Example D — Compare generations (conceptual)

ls -l /nix/var/nix/profiles/system-*-link | tail
# note store paths differ per generation

Example E — Logs during rebuild

sudo nixos-rebuild switch --flake .#lab -L --show-trace

Lab 1 — Inventory generations before chaos

sudo nix-env -p /nix/var/nix/profiles/system --list-generations
readlink -f /nix/var/nix/profiles/system

Record current generation number in notes.


Lab 2 — Mode drills (harmless package)

  1. Add a tiny package with boot mode only.
  2. Confirm it is not on PATH until reboot (or document actual behavior you observe).
  3. Reboot (VM snapshot first).
  4. Confirm package present.

Then:

  1. Use test for another package.
  2. Confirm available immediately.
  3. Reboot without switch and observe whether it persists as default—write what happened.

Lab 3 — Deliberate break → rollback

VM snapshot first.

Pedagogical break:

  1. switch a config that installs a known package cowsay.
  2. Then switch a config that removes it and sets a harmless env marker.
  3. rollback and show package/env difference.

Document generation numbers before/after.

Safer annoying-but-not-fatal changes beat eval failures (eval never creates a “bad generation”).


Lab 4 — Boot menu literacy

On next reboot:

  1. Enter boot loader menu (often hold space / show menu for systemd-boot).
  2. Identify generation entries.
  3. Boot current known-good.

Write how your hypervisor + loader expose this.


Lab 5 — Incident card

Create ~/nixos-config/docs/rollback.md:

# Rollback card
1. Console access method: …
2. List generations: …
3. Rollback command: …
4. Boot menu steps: …
5. Last known good generation: …

Exercises

  1. Fill a table: mode × (activates now?) × (changes boot default?).
  2. Perform build then inspect toplevel path without switching.
  3. Simulate remote-only caution: list changes you would only test first.
  4. After three switches, list gens and mark which you could GC later.
  5. Break SSH in a snapshotted VM; recover via console + rollback.
  6. Time a no-op rebuild vs a package-adding rebuild.
  7. Document difference between profile rollback and boot-menu boot.
  8. Add a “last good gen” field to your git commit message habit.
  9. Explain to a peer why DB data survives rollback (or doesn’t get restored).
  10. Practice absolute flake path rebuild under sudo.
  11. Read activation output for one switch; annotate three steps.
  12. Update rollback card after the boot chapter for loader keys.

Common pitfalls

Symptom Theory
Rollback “did nothing” for app data Data ≠ system gen
Locked out via SSH Use console; prefer test for net changes
No older generations GC’d or first install
test change vanished after reboot Expected if never switched
Flake path wrong under sudo Use absolute path or root’s cwd discipline
Eval error mistaken for rollback drill Need a successful bad generation
GC before verification Lost escape hatches

Checkpoint

  • Explain switch/test/boot/build
  • List system generations
  • Performed a rollback successfully
  • Boot menu generation selection documented
  • Incident card written
  • VM snapshot restored option still available

Further depth (this book)

Topic Chapter
Firewall safety with test Networking and firewall
Boot loader entries Boot and hardware
State vs generations Filesystems and state
GC hygiene fleet-wide Ops part