Nixos on Raspberry Pi 4

So I’ve been playing around with NixOS on a Raspberry Pi 4 that I have at the house. NixOS seems like a great idea, but getting it onto the Raspberry Pi 4 is something of a beast of a process. So I wanted to document here how I did it. Most of the process is the normal NixOS installation process, with a few tweaks at the very end.

My Pi’s Case is a Challenge

Rather than having a plain Raspberry Pi 4 and writing the SD image provided by the NixOS team to that image, I have my Pi in an [Argon One m.2](some link here) case. That case allows me to mount an m.2 SSD to an RPi4 B device over USB3. They’re pretty slick cases, decent quality, include full size HDMI ports and basic power management, and still allow GPIO header splitouts if you need them for your application. If you are looking for a handy little case that is metal, well built, functions as a heat sync, includes a tiny fan, and gives you the SSD USB3 split out, then I suggest you take a look at this case. They also have a version that does not have the SSD, but still gives you the port split out and is the same well built metal top enclosure.

One downfall of the case is that, when closed, it does not leave the SD card slot accessible. You can load an SD card in before you put the Pi into the case if you plan to always boot off one, but I wanted the significantly better speed that the SSD over USB3 can give compared to a plain SD card. So, before I loaded the Pi into the case I used the SD card slot to update the Pi firmware (my 4s are pretty old) and set it to default boot from USB by default.

I am going to operate under the assumption you have your Pi set to boot off of USB and already have a chosen device plugged in to the system.

NixOS Boot

NixOS provides two main ways to install. There is a bootable ISO file, and for machines like the RPi4 there is a pre-built SD card image. While it’s maybe possible to get the Aarch64 ISO to boot, I have not spent time working on that. I opted for a manual install based on the SD card image. But I did not use an SD card.

Get the SD Image

Get the latest version of the SD image. At the time of writing the latest release of NixOS is 21.11, so the latest versions of the images can be found in Hydra. From that page select the “Jobs” tab. Search the page for “sd_image”. I opted for the item called “nixos.sd_image.aarch64-linux”. There is also one that is called “sd_image_new_kernel”. I haven’t tried it, so I cannot tell you if it works.

Use your favorite tool to burn the image to a regular USB flash drive. I have used the Raspberry Pi Imager program as well as dd from the command line in Linux. There are other tools, but choose your favorite one to write it to a Flash Drive.

With the RPi4 turned off, insert the prepared Flash Drive to the RPi4’s USB port. I have one of the USB3 ports open, so that’s the one I selected. If necessary, select this drive. In my case, this Flash Drive ends up as the default option.

Parition the SSD

The SD Image will boot you to a terminal and auto log in as the “nixos” user. Now you need to partition the drive. You should have access to the lsblk tool that can list the drives. In my case the flash drive is /dev/sdb and the SSD is /dev/sda. If you are on a different system, adjust these commands appropriately. I started by wiping the SSD with wipefs.

sudo wipefs -f /dev/sda

Now you need to partition the SSD card. So let’s enter our partition utility

sudo fdisk /dev/sda

Create partition 1 starting from sector 2048, with size 500M.

n
p
1
2048
+500M

Create other partitions the way you want them laid out. I create one large root partition that takes up from 500M to within 8GB of the end of the drive. The last 8GB I reserve for swap. So these commands create those two partitions.

n
p
2
<enter>
-8G

n
p
3
<enter>
<enter>

Now set the partition types

t
1
0b
t
3
82

0b is the code for W95 FAT32 and 82 is the type for Linux Swap. Now, save the partition table and exit the program with command w. Be sure the filesystems are synced.

sudo sync

Format the drives

Format the partitions you created. These are the ones I use:

sudo mkfs.vfat /dev/sda1
sudo mkfs.ext4 /dev/sda2
sudo mkswap /dev/sda3
sudo swapon /dev/sda3

Obviously you can use whatever format you want for your Linux partitions and you might have skipped a swap partition. But that first partition needs to be a vfat partition to store the UEFI code.

Mount the drive

Now, create the mountpoint where the target system will be installed.

sudo mkdir -p /mnt
sudo mount /dev/sda2 /mnt
sudo mkdir -p /mnt/boot
sudo mount /dev/sda1 /mnt/boot

Generate and Customize Config

If you have your own way to fetch NixOS Configuration files, go ahead and fetch those to /mnt/etc/nixos. If you are new to NixOS, then just skip that bit.

Generate the base configuration:

sudo nixos-generate-config --root /mnt

The general process of configuration and is beyond this, but be sure you have at least a text editor of some kind in your installation! Now go to /mnt/etc/nixos/ and edit the configuration to your heart’s desire. The following settings are great for a Raspberry Pi 4:

{
	boot = {
		# The Linux kernel version
		kernelPackages = pkgs.linuxPackages_rpi4;
		# Options to enable serial console and more
		kernelParams = [
			"8250.nr_uarts=1"
			"console=tyAMA0,115200"
			"console=tty1"
			"cma=128M"
		];

		# The bootloader
		loader = {
			raspberryPi = {
				enable = true;
				version = 4;
			};

			# Use extlinux, whereas the NixOS default is Grub
			grub.enable = false;

			# Enables the generation of /boot/extlinux/extlinux.conf
			generic-extlinux-compatible.enable = true;
		};
	};

	environment.systemPackages = with pkgs; [
		raspberrypifw
	];
}

Install

Normally in NixOS you would now run the basic installer sudo nixos-install --root /mnt. However, the SD image does not have any of the NixOS channels installed and it lacks the space necessary to sync them in the default edition. So now we have to run the command in a slightly different way.

sudo nixos-install --root /mnt -I https://channels.nixos.org/nixos-21.11-aarch64/nixexprs.tar.xz

Copy the firmware

Raspberry Pis require special firmware. By default this does not get installed into the /boot partition where UEFI needs to find it. However, it is in the Flash drive that you are already running. So let’s copy it over. In my system, remember that the Flash drive is /dev/sdb. So for me this process looks like this

sudo mkdir -p /firmware
sudo mount /dev/sdb1 /firmware
sudo cp -r /firmware/* /mnt/boot

This should allow you to boot the system off of the SSD. Now power off the system, remove the Flash drive, and boot.

Enjoy

Reboot into NixOS, login as root with the password you created during install, and set any password for users you may have created in your configuration.