NixOS; what's in a rebuild?

Published 2020-09-06 on Farid Zakaria's Blog

I have been using Nix but mainly through home-manager on my Debian system; finally I made the plunge into running NixOS on an AWS server for my side-projects.

There’s a lot of information on how to configure & setup an already created NixOS machine but not much advice for workflows, best practices & multiple machines.

Here I’ll document what I found useful and pulling back the veil on some of the NixOS tooling.

Feel free to check my Nix repository for home-manager & NixOS https://github.com/fzakaria/nix-home

configuration.nix

NixOS documentation outlines that the entry-point to the NixOS setup is a file configuration.nix; why?

Well, NixOS configuration is primarily driven by nixos-rebuild; let’s take a look at the source.

One of the first things it does when running switch is the following.

...
pathToConfig="$(nixBuild '<nixpkgs/nixos>' \
--no-out-link -A system "${extraBuildFlags[@]}")"
...

Interesting so it’s building a system & fetching the /nix/store/ path for it; what’s that?

You can find the system attribute in nixos/default.nix.

{ configuration ? import ./lib/from-env.nix "NIXOS_CONFIG" <nixos-config>
, system ? builtins.currentSystem
}: {
  ...
  system = eval.config.system.build.toplevel;
  ...
}

This is in fact the entry-point for NixOS; and we can see here that the configuration defaults to if not given.

On NixOS <nixos-config> is set in NIX_PATH to nixos-config=/etc/nixos/configuration.nix

Let’s write the most basic configuration.nix

{...}:{
  # We need no bootloader, because we aren't booting yet
  boot.loader.grub.enable = false;

  fileSystems = {
     "/".label = "nixos-root";
  };
}

$ nix-build -I nixos-config=./configuration.nix --no-out-link '<nixpkgs/nixos>' -A system

We can also inline and build the system.

let nixos = import <nixpkgs/nixos> { configuration = {
      # We need no bootloader, because we aren't booting yet
      boot.loader.grub.enable = false;

      fileSystems = {
         "/".label = "nixos-root";
      };
    };
};
in nixos.system

The output will be the /nix/store system closure; this is a somewhat typical Linux filesystem including /etc.

comment from infinisil: /bin & /lib are purposefully left out to avoid programs depending on them; forcing purer Nix builds. They can be found nested within the /sw directory.

$ tree /nix/store/x0nbdy16myi7y72vy02nw8hywr3fnv7d-nixos-system-nixos-20.09pre237891.f9eba87bf03

/nix/store/x0nbdy16myi7y72vy02nw8hywr3fnv7d-nixos-system-nixos-20.09pre237891.f9eba87bf03
├── activate
├── append-initrd-secrets -> /nix/store/vy8lxijna11za631r54gb9gl099qn7by-append-initrd-secrets/bin/append-initrd-secrets
├── bin
│   └── switch-to-configuration
├── configuration-name
├── etc -> /nix/store/cbg97bmc5jhid2hn0xxgs5ggd75xcibb-etc/etc
├── extra-dependencies
├── firmware -> /nix/store/76n4kcg49px9wqha2d9lpfsj5cccwj0h-firmware/lib/firmware
├── init
...

Cool! nixos-rebuild will change /run/current-system pointing to this entry afterwards.

vm.nix

A little fun tip; there is a top level attribute alongside system that can make building a virtual machine very easy.

We simply need to change the attribute to nixos.vm

nixos = import <nixpkgs/nixos> { configuration = import ./configuration.nix };
in nixos.vm

You can then start the vm by running ./result/bin/run-nixos-vm.

The virtual machine setup will override the hardware configuration to setup sane defaults for QEMU

I tend to keep a vm.nix alongside each of my machine configurations to test quickly in an isolated environment.

If you prefer a nix-build one-liner rather the explicit file above; you can do nix-build '<nixpkgs/nixos>' -A vm -I nixos-config=./configuration.nix which accomplishes the same thing.