Nix Generations and Rollbacks

Luis Michael Ibarra - - 4 mins read

One of the coolest features of Nix is the ability to rollback to a previous state. This is done by generations.

Generations

Generations are a way to keep track of the state of the system. Each time a change is made to the system, a new generation is created. This allows you to rollback to a previous state if something goes wrong.

A generation is a snapshot of the system at a specific point in time. It includes all the packages that were installed at that time, as well as the configuration files that were in place.

Generations can be listed in a couple of ways. nix-env --list-generations command lists the generations for the user profile. nix-env --list-generations --profile /nix/var/nix/profiles/system lists the generations for the system profile.

System profile is the profile that is used by the system itself. It is the profile that is used when the system boots up. User profile is the profile that is used by the user. It is the profile that is used when the user logs in.

Generations can be found in /nix/var/nix/profiles/. Each stored generation includes a system and user profile. The system profile is used by the system itself, while the user profile is used by the user.

When using flakes and nixos-rebuild, a new generation is created each time the system configuration is changed. This allows you to rollback to a previous state if something goes wrong.

Rollbacks

Rollbacks are the process of reverting to a previous generation. This can be done using the nix-env --rollback command. This command will revert the system to the previous generation. However, when using flakes and nixos-rebuild, the rollback is done using the nixos-rebuild switch --rollback command or checking out the previous and running `nixos-rebuild switch which I encourage to use.

Rollbacks are useful when something goes wrong with the system configuration. For example, if a new package is installed that causes the system to crash, you can rollback to the previous generation to restore the system to a working state.

Rollbacks are also useful when testing new configurations. If you make a change to the system configuration that causes a problem, you can rollback to the previous generation to restore the system to a working state. For instance, installing a particular kernel version to fix ip6tables so you can run cilium or tailscale in your ipv6 network.

/run/current-system/ and /run/booted-system/ are symlinks to the current and booted system generations respectively. Both should be the same unless you are in the middle of a system rebuild.

A nice way to see the differences between generations is diff <(ls -l /run/current-system/ | sort) <(ls -l /run/booted-system/ | sort) which shows the differences between two generations. This can be useful when trying to debug a problem with the system configuration.

Learnings

Flakes over nix-env

Use flakes and nixos-rebuild for system configuration changes. Use nix-env for user profile changes. Use nix-env --rollback for user profile rollbacks. Avoid using nixos-rebuild switch --rollback for system profile rollbacks; instead, checkout the previous generation and run nixos-rebuild switch.

Pin your commit in your system generation

When using flakes, it is a good idea to pin the commit hash in your system generation. This will ensure that the system is always built from the same commit, making it easier to rollback to a previous state if something goes wrong.

You can use the system.nixos.label option in your flake.nix to pin the commit hash. For example:

{
  description = "My NixOS configuration";
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  outputs = { self, nixpkgs }: {
    nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [ ./configuration.nix ];
      configuration = { allowUnfree = true; };
      nixosConfig = {
        system.nixos.label = "my-nixos-config";
      };
    };
  };
}

Use nixos-rebuild switch instead of nixos-rebuild boot

When making changes to the system configuration, use nixos-rebuild switch instead of nixos-rebuild boot. This will apply the changes to the current generation, activate it, and make it the default boot option so any it can be tested and verified. nixos-rebuild boot will apply the changes but it will not activate until the next boot putting in you in a bad spot if you are not able to boot and don’t have other generatios to rollback on. More info in nixos-rebuild.

Clean up old Generations

Generations can take up a lot of disk space. It is a good idea to clean up old Generations to free up disk space. This can be done using the nix-collect-garbage command. this command will remove all the generations that are no longer needed. However, be careful when using this command as it can remove generations that are still needed. Use the --delete-older option to only remove generations that are older than a certain number of days. Also, use nix.gc options to run garbage collection automatically.