(Re-)Installing NixOS

I’ve been using NixOS for quite a while, but didn’t really commit to it at first. This resulted in Windows taking up most of my disk space and I tried to be clever with partitioning too .. which bit me a lot. And that’s the main reason for reinstalling NixOS this weekend: I need disk space! My root partition has a size of 10G, because that ought to be enough, when I’m saving large stuff like games elsewhere, right? As it turns out, that’s not the case for NixOS. And it makes sense: If you want to rollback, you need to keep the necessary data around. And even if you run the garbage collection every time you switch to a good looking generation, it still takes free space about the size of the current system to upgrade it. Meaning with a 10GB root partition I can update a system of 5GB without hassle. And as I have reached that point I need to first remove big packages, upgrade, run the garbage collector and then readd those packages. Anyway .. mistakes were made, on to a hopefully better approach!

I’m mostly following this gist and its comments and have taken some inspiration from this blog post.

The first thing is booting the current NixOS iso. I like the graphical one because the following takes many hours, during which you can then at least use Firefox. I wasn’t too confident to have backed up everything important, so a backup is necessary:

dd if=/dev/sda bs=8M status=progress of=/root/data/ssd-backup.bin

In this case I mounted an external drive at /root/data and don’t compress the image, so I can easily mount it later. Now that we have the backup, it’s best to erase its traces:

dd if=/dev/zero of=/dev/sda bs=4096 seek=0 status=progress

Chosing /dev/zero as source and overwriting the drive only once appears to be suboptimal, but this takes long enough already (~50MB/s) and for me it is good enough. Next up is reformating the drive:

gdisk /dev/sda

I’ve created a 1G EFI partition (type EF00) and the rest as a Linux reserved partition (type 8301) as there doesn’t seem to be proper type for LUKS. Speaking of LUKS: Let’s continue with setting up the encryption.

cryptsetup luksFormat /dev/sda2
cryptsetup luksOpen /dev/sda2 enc-pv

Then we initialize the decrypted partition as physical volume for LVM, create a volume group and logical volumes. I’ve been bitten by bad partitioning and will try my luck with btrfs subvolumes here. Btrfs doesn’t support a swap file, so I’ll need a swap partition to be able to hibernate the system.

pvcreate /dev/mapper/enc-pv
vgcreate vg /dev/mapper/enc-pv
lvcreate -L 400G -n root vg
lvcreate -l '100%FREE' -n swap vg

With that the partitions are all set up and can now be formatted.

mkfs.fat -F 32 -n BOOT /dev/sda1
mkfs.btrfs -L root /dev/vg/root
mkswap -L swap /dev/vg/swap

Finally the drive is ready for the actual NixOS installation!

mount /dev/sda1 /mnt/boot
swapon /dev/vg/swap
mount -t btrfs /dev/vg/root /mnt
btrfs subvol create /mnt/nixos
umount /mnt
mount -t btrfs -o defaults,ssd,noatime,compress=zstd,subvol=nixos /dev/vg/root /mnt

So I created a btrfs subvolume for NixOS and mount it with options for SSDs and compression. With everything mounted, the installation can now really start.

nixos-generate-config --root /mnt

I’ve had to change my configuration a bit for the new drive layout and could just run the installer without issues. The generated hardware-configuration.nix seemed to be missing some important options, so do make sure everything is right there. If not you’ll notice when booting .. and can repeat the mounting and installation process (in my case the volume group was inactive which can be fixed via vgchange -a vg). Well, that’s it, NixOS is now successfully reinstalled! Except that it doesn’t generate boot entries for new generations. nixos-generate-config set boot.loader.efi.canTouchEfiVariables to true, which worked fine during the installation, but somehow causes this problem. With this option deactivated I’m now happy to have a running system again, with which I can actually do some work.

Jonas Beyer
Software Engineer