DALL·E generated image using the request: Nixos Linux install on btrfs setup with impermanence also called Erasing My Darlings
DALL·E: Nixos Linux install on btrfs setup with impermanence also called Erasing My Darlings

NixOS is a Linux distribution that is built around the Nix package manager. One of the unique features of NixOS is its ability to declaratively manage the configuration of your system. This means that you can specify the desired state of your system, and NixOS will automatically ensure that the system is in that state.

In this blog post, we will show you how to set up a NixOS system on LUKS and btrfs, with an “erase your darlings” setup. In this setup, the root filesystem is mounted as tmpfs, and all persistent data is stored on btrfs subvolumes.

First, you will need to create a bootable NixOS installation media, such as a USB drive. Downloads and instructions are well documented on the NixOS website. Once you have done this, boot your system from the installation media and follow the prompts to begin the installation process.

The basic install steps are described in the NixOS manual.

When prompted, the user must create a valid partition layout. Recommended partition layouts for UEFI (or MBR) are well described in the NixOS documentation, see here. Only a boot (EFI / fat32) and root (btrfs) partitions are required.

The root partition will be encrypted using LUKS.

cryptsetup -y -v luksFormat /dev/<root-partition>

Now open the encrypted volume and mount the btrfs filesystem to a temporary location:

cryptsetup open /dev/<root-partition> root

mkfs.btrfs /dev/mapper/root

mkdir /tmp/root
mount /dev/mapper/root -o compress-force=zstd,noatime,ssd /tmp/root

Next, you will need to configure the btrfs subvolumes. In the subvolume layout, create at least subvolumes for nix, home, persist, nixos-config and (other) persistent folders (more on this later).

cd /tmp/root
btrfs subvolume create nix
btrfs subvolume create home
btrfs subvolume create persist
btrfs subvolume create nixos-config

Now it is time to build the NixOS system layout. First the root is a tempfs (resides in memory). In the root the boot, nix, home, persist, and nixos-config must be mounted.

mount -t tmpfs none /mnt

mkdir /mnt/{boot,nix,home,persist}
mkdir /mnt/etc/nixos

mount /dev/<boot-partition> /mnt/boot

mount /dev/mapper/root -o compress-force=zstd,noatime,ssd,subvol=nix /mnt/nix
mount /dev/mapper/root -o compress-force=zstd,noatime,ssd,subvol=home /mnt/home
mount /dev/mapper/root -o compress-force=zstd,noatime,ssd,subvol=persist /mnt/persist

mount /dev/mapper/root -o compress-force=zstd,noatime,ssd,subvol=nixos-config /mnt/etc/nixos

Time to generate the basic NixOS configuration: nixos-generate-config --root /mnt. Now adjust the /mnt/etc/nixos/configuration.nix to your liking, see the NixOS manual. Adjust the /mnt/etc/nixos/hardware-configuration.nix to something like this:

  boot.initrd.luks.devices."root".device = "/dev/disk/by-uuid/<your uuid>";

  # Filesystems (root on tmpfs)
  fileSystems."/" =
    { device = "none";
      fsType = "tmpfs";
      neededForBoot = true;  # required
    };

  fileSystems."/nix" =
    { device = "/dev/mapper/root";
      fsType = "btrfs";
      options = [ "defaults" "compress-force=zstd" "noatime" "ssd" "subvol=nix" ];
      neededForBoot = true;  # required
    };

  fileSystems."/etc/nixos" =
    { device = "/dev/mapper/root";
      fsType = "btrfs";
      options = [ "defaults" "compress-force=zstd" "noatime" "ssd" "subvol=nixos-config" ];
    };

  fileSystems."/persist" =
    { device = "/dev/mapper/root";
      fsType = "btrfs";
      options = [ "defaults" "compress-force=zstd" "noatime" "ssd" "subvol=nx-persist" ];
      neededForBoot = true;
    };

  fileSystems."/home" =
    { device = "/dev/mapper/root";
      fsType = "btrfs";
      options = [ "defaults" "compress-force=zstd" "noatime" "ssd" "subvol=home" ];
    };

 fileSystems."/boot" =
    { device = "/dev/disk/by-uuid/1E94-4A2A";
      fsType = "vfat";
    };

Also in the hardware-configuration.nix the impermanence module should be set up:

{ config, lib, pkgs, modulesPath, ... }:
let
  impermanence = builtins.fetchTarball "https://github.com/nix-community/impermanence/archive/master.tar.gz";
in
{
  # All other settings
  # ...

  # this folder is where the files will be stored (don't put it in tmpfs)
  environment.persistence."/persist" = { 
    directories = [
      "/etc/ssh"
      "/var/log"
      "/var/lib/cups"
      "/var/lib/fprint"
      "/var/db/sudo/lectured"
    ];
    files = [
      "/etc/machine-id"
      "/etc/nix/id_rsa"
      "/var/lib/cups/printers.conf"
      "/var/lib/logrotate.status"
    ];
  };

  # Even more settings
  # ...
}

Of course, other usecases might require other folders and files to be stored via impermanence.

I have chosen to store specific items manually via btrfs subvolumes. For example; I store the folder /etc/NetworkManager on a subvolume. This way, the folder is also retained between boots, but that retainance is then manually managed (which I prefer). Another example is the docker folder /var/lib/docker, which I store in a btrfs subfolder. Make sure to also mount those folders. See the hardware-configuration.nix example above for how to do this.

Specific items of interest in the configuration.nix are the following:

  # Don't allow mutation of users outside of the config.
  users.mutableUsers = false;
  users.users.root.initialHashedPassword = "HASHEDPASSWORD";  # hash via the mkpasswd utility

With all this setup done and finished, the actual installation can start: nixos-install.

Reboot.

Now that the system is up and running. NixOS makes it easy to experiment with different configurations and software without fear of breaking your system. The impermanence module and manual btrfs subvolumes make it easy to retain specific aspects between reboots. All the rest will be reset and regenerated… and so we always start with a fresh system.

In summary, with NixOS, you can easily set up a system that uses LUKS encryption and btrfs with an “erase your darlings” setup, which allows you to keep your root filesystem as tmpfs and persist your data on btrfs subvolumes and the impermanence module.

Links of interest: