NixOS System Generations: Declarative Rebuilds With Instant Rollback
NixOS makes the running system a pure function of configuration.nix. nixos-rebuild switch builds an immutable generation and updates the bootloader atomically, giving instant rollback without manual restores.
20 Mar 2026, 20:18 UTC

Production changes that vanish on reboot are a familiar pain. You SSH in, edit a service file by hand, it works, and weeks later a teammate runs a rebuild and the change is gone. NixOS solves this by making the running system a pure function of a single configuration expression. The useful takeaway is that nixos-rebuild switch does not patch the live system; it builds a new immutable generation and atomically points the bootloader at it.
The drift problem imperative admins hit
Traditional Linux administration is imperative: install packages, edit files, restart services. State accumulates in /etc, in the package database, and in the memory of whoever touched what. Reproducing that state on another host requires a runbook, not a file.
NixOS replaces that with a declarative model. The desired state lives in Nix expressions, typically /etc/nixos/configuration.nix or a flake. The Nix language is functional and pure, so the same expression should evaluate to the same system closure on any machine. Imports let you split concerns into modules without scattering edits.
How nixos-rebuild switch creates an atomic generation
The Nix store holds immutable paths like /nix/store/<hash>-name. Packages and configuration are never mutated in place. When you run a rebuild, Nix evaluates the expression, builds only what changed, and assembles a new system closure.
Run this on the NixOS host as root or via sudo:
sudo nixos-rebuild switchOr, with a flake:
sudo nixos-rebuild switch --flake .#<hostname>The command builds the new closure, then updates the /nix/var/nix/profiles/system symlink to point at it. The bootloader entry is updated so the next boot uses the new generation. The switch is atomic: the old generation remains intact on disk.
Each rebuild creates a new generation. List them with:
sudo nix-env --list-generations -p /nix/var/nix/profiles/systemA practical check is to reboot and confirm the bootloader menu (GRUB or systemd-boot) shows multiple NixOS entries. Selecting an older entry boots the previous generation without reinstalling anything.
Rollback is therefore a bootloader choice, not a restore from backup. Risk remains: a bad kernel or initrd can make a generation unbootable, and manual edits outside the Nix store are ignored on the next rebuild.
A concrete declarative example
Configuration is data, not scripts. A minimal module can declare a user and a service:
{ config, pkgs, ... }:
{
users.users.alice = {
isNormalUser = true;
extraGroups = [ "wheel" "networkmanager" ];
shell = pkgs.zsh;
};
services.nginx = {
enable = true;
virtualHosts."localhost" = {
root = "/var/www/html";
};
};
}With this in configuration.nix, or split into files pulled in via imports = [ ./users.nix ./services.nix ];, running sudo nixos-rebuild switch will create the user account if missing, enable nginx, and place the generated service files under the Nix store. No apt install or manual /etc/nginx/nginx.conf edits are required.
Expected checks after the rebuild: the user appears in /etc/passwd, systemctl status nginx reports active, and the unit file references read-only paths under /nix/store. Manual changes to nginx config files will be overwritten on the next rebuild, which is the point.
Trade-offs and practical limits
The learning curve for the Nix expression language is steep compared to YAML or shell scripts. Purity also means quick imperative tweaks made directly on the filesystem are lost or ignored on rebuild, which surprises operators used to hot fixes.
Disk usage grows with generations because each rebuild keeps the previous closure. Prune old generations with:
sudo nix-collect-garbage -dThis is destructive to old states, so list generations first and confirm which ones you still want as rollback targets. You can also delete specific generations with nix-env --delete-generations against the system profile.
Flakes add reproducibility by pinning inputs, but they add indirection and remain an opt-in workflow. The behavior described here is well established for NixOS with systemd-boot or GRUB and the classic nixos-rebuild path; verify against your NixOS release's manual before relying on details.
Actionable next step
Start with a single host or a VM. Move one service into configuration.nix, commit the file, run sudo nixos-rebuild switch, and list generations. Keep manual changes out of the store and inside the expression, treat the bootloader menu as your rollback plan, and schedule garbage collection to control disk growth.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.