Magic Deployments with nixos-rebuild

title image of blog post \
📆
July 31, 2023 by Jacek Galowicz

One of the things you learn after installing NixOS for the first time is that all these tasks are basically the same from a tooling perspective: Adding/removing a package, massively changing the system configuration, or simply updating it. After changing the configuration and/or the inputs, you run nixos-rebuild and the system is deployed. If the configuration turns out to be bad - it can simply be rolled back! But how does this tool work and what other cool features does it have?

The Nix & NixOS 101 class teaches how to install NixOS and how to experiment with NixOS configurations using nixos-rebuild. As this class is already packed with so many other useful things like how to write NixOS modules and NixOS integration tests, we are only scratching the surface of what nixos-rebuild can do!

What is nixos-rebuild?

nixos-rebuild is the standard tool for altering the system configuration of an installed NixOS system. The typical steps are, as described in the NixOS documentation:

  1. Change NixOS configuration
    • by changing the configuration in your flake.nix file
    • by updating the inputs with nix flake update
  2. Run sudo nixos-rebuild switch

That’s it. You might want to reboot the system if the kernel was updated.

How Does It Look Like?

Let’s perform a full system upgrade:

$ nix flake update --commit-lock-file
warning: updating lock file '/home/tfc/src/nixos-configs/flake.lock':
 Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/ac1acba43b2f9db073943ff5ed883ce7e8a40a2c' (2023-07-23)
   'github:NixOS/nixpkgs/f3fbbc36b4e179a5985b9ab12624e9dfe7989341' (2023-07-26)
warning: committed new revision 'ba119606b5ca47f43b8fa338bd02fe37aa6d405f'

The --commit-lock-file argument is optional but handy if the configuration is stored in a git repository. If you are going to commit the lock file anyway, nix flake update can commit it for you.

After an update, the system typically needs to be rebuilt even without other configuration changes:

$ sudo nixos-rebuild switch
building the system configuration...
stopping the following units: accounts-daemon.service, cpufreq.service, systemd-udevd-control.socket, systemd-udevd-kernel.socket, systemd-udevd.service
NOT restarting the following changed units: display-manager.service, libvirt-guests.service, systemd-fsck@dev-disk-by\x2duuid-091D\x2d4B27.service
activating the configuration...
warning: not applying GID change of group ‘localtimed’ (990 -> 325) in /etc/group
warning: not applying UID change of user ‘localtimed’ (993 -> 325) in /etc/passwd
setting up /etc...
reloading user units for tfc...
setting up tmpfiles
reloading the following units: dbus.service
restarting the following units: home-manager-tfc.service, nix-daemon.service, polkit.service, sshd.service
starting the following units: accounts-daemon.service, cpufreq.service, systemd-udevd-control.socket, systemd-udevd-kernel.socket
the following new units were started: libvirtd.service

If the new configuration is not good for some reason, it can be rolled back:

$ sudo nixos-rebuild switch --rollback
[sudo] password for tfc:
switching profile from version 359 to 358
stopping the following units: accounts-daemon.service, cpufreq.service, systemd-udevd-control.socket, systemd-udevd-kernel.socket, systemd-udevd.service
NOT restarting the following changed units: display-manager.service, libvirt-guests.service, systemd-fsck@dev-disk-by\x2duuid-091D\x2d4B27.service
activating the configuration...
warning: not applying GID change of group ‘localtimed’ (990 -> 325) in /etc/group
warning: not applying UID change of user ‘localtimed’ (993 -> 325) in /etc/passwd
setting up /etc...
reloading user units for tfc...
setting up tmpfiles
reloading the following units: dbus.service
restarting the following units: home-manager-tfc.service, nix-daemon.service, polkit.service, sshd.service
starting the following units: accounts-daemon.service, cpufreq.service, systemd-udevd-control.socket, systemd-udevd-kernel.socket
the following new units were started: libvirtd.service

The activation of a new/old configuration takes only seconds depending on how many services need to be restarted.

What Other Features Does nixos-rebuild Provide?

nixos-rebuild provides functionality on two different axes:

The remote building capabilities of nixos-rebuild are very interesting, too. We will have a look at them in the next blog article.

So what are the command targets? These are the most interesting ones for day-to-day use:

nixos-rebuild ... Functionality
switch Builds and activates the system configuration with immediate effect. In addition to that, it installs the configuration in the bootloader.
test Like switch, but does not update the bootloader list.
boot Builds the config and updates the bootloader list without activating the system immediately.
dry-run Only evaluates the config without building it.
dry-activate Builds the config and print which changes would be performed on the current system during activation.
build-vm Builds the config and generates a script that starts this configuration as a VM on your local system.

The build-vm part is interesting - let’s try it out:

$ nixos-rebuild build-vm
building the system configuration...

Done.  The virtual machine can be started by running /nix/store/yscmmshi2q0dmr6xcfdnhz3829sd5lqz-nixos-vm/bin/run-jongepad-vm

$ ./result/bin/run-jongepad-vm
Disk image do not exist, creating the virtualisation disk image...
Formatting '/home/tfc/src/nixos-configs/jongepad.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16
Virtualisation disk image created.

Running the script starts the VM and after boot, it shows the same login manager as my laptop does:

The nixos-rebuild build-vm VM in action

Of course, we can’t really log in without setting a password first, because the user passwords are not part of the NixOS configuration. During the installation of a local NixOS system, we set them imperatively. Remote system deploys typically use SSH key authorization instead of passwords.

Other Interesting Parameters

--rollback

With every activation, the path /nix/var/nix/profiles/system is set to the new configuration. For every activated configuration, nix adds another symlink /nix/var/nix/profiles/system-XYZ-link to the profiles folder, where XYZ is the number of the current system. Each of these systems is considered another NixOS system generation.

When --rollback is used in combination with switch, test, or boot, nixos-rebuild does not build and activate a new system version but reactivate the last one.

--flake

Adding --flake path/to/flake/repo#systemName to the command line tells nixos-rebuild to use the flake at path path/to/flake/repo and use the system configuration with the name systemName.

If the #systemName part is omitted, nixos-rebuild tries to deduce it from the local hostname.

The --flake parameter can be omitted if the system has a /etc/nixos/flake.nix flake file. The system name is also automatically deduced from the host name in this case.

--fast / --no-build-nix

To make sure that the latest version of nix is used for the deployment, nixos-rebuild obtains the latest pkgs.nixUnstable version. Using this flag can accelerate the process by trusting that the currently installed version is compatible.

MacOS Users also need to use this flag when remote-deploying to a NixOS system from their Mac.

--profile-name

It is possible to create system configurations with specific profile names, like for example “maintenance”, or “lanparty”. These are installed into the bootloader next to the default system profile and can be selectively booted when needed.

Summary & Outlook

Installing, removing, upgrading, and configuring packages and system services is painless on NixOS: Instead of tampering with the system state by imperatively installing packages and manipulating service configurations, we have a central NixOS configuration that can be versioned and describes it all.

Given any NixOS configuration, we can build and switch to this configuration with a single command using nixos-rebuild. If the new config turns out to be bad, we can simply --rollback any time, or boot into the last system generation! Even before doing that, we can dry-activate or build-vm to test if our config makes sense even before deploying it to a real system.

The next article shows how to use nixos-rebuild to remotely deploy NixOS configurations on target machines via network. In addition to that, we will see how to remote build configurations on other hosts via network, may that be for performance reasons or cross-compilation.