Stop Server Drift: A Practical Guide to NixOS Declarative Rebuilds
Learn how NixOS replaces ad-hoc server management with a single declarative configuration file, how nixos-rebuild builds and activates system generations, and how to roll back safely when a change fails.
28 Sept 2026, 23:01 UTC

You know the pattern. You spin up a server, install packages with apt or dnf, tweak a config file, and everything works. Two weeks later, a teammate asks how the firewall was set up, and the honest answer is that you ran a command from a chat thread and edited a file, but you no longer remember which lines changed. When the disk dies, rebuilding from memory takes days.
The useful takeaway: if your system is described in a file you can read, version, and replay, you stop guessing. NixOS pushes this idea to its logical conclusion by treating the entire operating system as a single declarative expression.
Declarative Configuration: Describe the End State
On most Linux distributions, system state is the result of accumulated imperative commands. NixOS inverts this. The primary source of truth is /etc/nixos/configuration.nix, a file written in the Nix expression language. Instead of saying "install nginx and then start it," you declare that the nginx service should be enabled. The system figures out the rest.
This file is not a shell script that mutates the live system. It is a pure description of desired state. You can import additional modules, share snippets across machines, and keep the whole thing in Git. Because the description is pure, the same file evaluated on two identical machines should produce the same system closure.
nixos-rebuild: From Expression to Running System
The bridge between your text file and a running kernel is nixos-rebuild. Run it as root on the NixOS host. It evaluates your configuration, builds any missing packages and configuration files into the read-only /nix/store, and then activates the result.
Three modes matter most:
nixos-rebuild testbuilds and activates the new configuration immediately, but does not add it to the boot menu. If you reboot, you return to the previous default. This is the safest way to validate a change.nixos-rebuild switchdoes everythingtestdoes and also makes the new configuration the boot default.nixos-rebuild buildonly builds the system closure without activating it, useful for catching errors in CI.
Because builds are incremental, unchanged packages are not rebuilt. Still, large configurations or first-time package builds can take minutes.
Worked Example: Enabling SSH and Testing a Failure
Assume a fresh NixOS installation. You want SSH access for a normal user. Edit /etc/nixos/configuration.nix as root and add the following:
{ config, pkgs, ... }:
{
imports = [ ./hardware-configuration.nix ];
boot.loader.systemd-boot.enable = true;
networking.hostName = "nixos-host";
services.openssh.enable = true;
users.users.alice = {
isNormalUser = true;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3Nza... alice@laptop"
];
};
}
Validate it without committing to the boot menu:
sudo nixos-rebuild test
If the build succeeds, the new generation is active. Verify the service and generated configuration:
systemctl status sshd
ls -l /etc/ssh/sshd_config
Expected checks: sshd should show as active, and /etc/ssh/sshd_config should be a symlink into /nix/store, proving the file was generated from your declaration rather than edited by hand.
Now introduce a deliberate error to see the safety net. Add a misspelled package:
environment.systemPackages = [ pkgs.nginx-stabl ];
Run sudo nixos-rebuild test again. Evaluation fails with an error indicating the attribute does not exist. No new generation is activated, and your running system is untouched. This is the core value of the model: a broken description does not break the machine.
Once you are satisfied, make the change permanent:
sudo nixos-rebuild switch
Generations: Your Undo Button
Every successful switch creates a new generation. Generations are listed in the systemd-boot menu at startup. If a change breaks remote access, you can revert the configuration without a rescue disk:
sudo nixos-rebuild switch --rollback
This activates the previous generation and makes it the new default. Note that rollback reverts system configuration, not user data or files written outside the Nix store.
Trade-offs and Limitations
The Nix expression language has a real learning curve. A missing semicolon or misspelled attribute produces an evaluation error that can feel cryptic until you learn to read the stack trace. The ecosystem is powerful, but you are trading familiar shell scripting for a lazy, functional language.
Rebuild times are the other cost. While incremental builds help, a configuration that pulls in many large packages—or a channel update that changes many dependencies—can take a long time to evaluate and build. On a remote server, plan for a maintenance window or use nixos-rebuild boot to stage the change for the next restart.
Start Small, Commit Often
You do not need to migrate a datacenter overnight. Pick one machine, put /etc/nixos under version control, and add one service at a time. Use nixos-rebuild test for every change until you trust it, and treat switch as the commit that survives reboot. The goal is not perfection on day one; it is a system that remembers what you told it, and nothing you forgot.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.