Installing Nixos On A Raspberrypi 4
I’m writing this blog post to document a successful NixOS setup on my Raspberry Pi 4 in case I somehow break it and because the issues I encountered were not covered in other blog posts or wiki pages.
This is not a step-by-step tutorial, reading until the end will allow you to skip a lot of issues.
Material
- Raspberry Pi 4 (or 5)
- micro SD card or USB storage (this will be the location of
/nix/store
) - External display + keyboard (to enable SSH, you’ll need to build your own image from this)
I’ll use a USB SSD because my Raspberry Pi’s SD card reader broke a long time ago (I get a nice warning about wrong voltage in the card reader now…).
Booting the Raspberry Pi 4 with USB may require a firmware update beforehand. From Raspberry Pi OS, simply run: sudo rpi-update
Get the image
First, we’ll get the NixOS image. This is the one I used at the time of writing, you should have a similar experience if you use it too.
wget https://hydra.nixos.org/build/295616998/download/1/nixos-image-sd-card-25.05pre788136.8a2f738d9d1f-aarch64-linux.img.zst
unzstd -d nixos-image-sd-card-25.05pre788136.8a2f738d9d1f-aarch64-linux.img.zst
See here for more recent versions.
Why this specific image
If you’re wondering why I’m using an “sd-card” image even though I boot from USB, it’s because this image is designed for the Raspberry Pi. It directly holds the system instead of containing an installer. See the related nix module:
This module creates a bootable SD card image containing the given NixOS configuration. The generated image is MBR partitioned, with a FAT /boot/firmware partition, and ext4 root partition. The generated image is sized to fit its contents, and a boot script automatically resizes the root partition to fit the device on the first boot.
The firmware partition is built with expectation to hold the Raspberry Pi firmware and bootloader, and be removed and replaced with a firmware build for the target SoC for other board families.
If I read this before, I could have just used the module to make an SD card image from my config. But I still needed some experimentation to get a working config anyway.
Install the image
Find out your device name with lsblk
.
sudo dd if=IMAGE.img of=/dev/sdX bs=4096 conv=fsync status=progress
Once the image is written, we can boot the Raspberry Pi up.
Problems
No nix channel
The very first nix related command I ran gave me this kind of error:
$ nix-shell -p git
warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels' does not exist, ignoring
error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I)
at «string»:1:25:
1| {...}@args: with import <nixpkgs> args; (pkgs.runCommandCC or pkgs.runCommand) "shell" { buildInputs = [ (nix-info) ]; } ""
| ^
(use '--show-trace' to show detailed location information)
Not nice, but probably fixable.
So I tried adding some channels:
$ nix-channels --add https://nixos.org/channels/nixpkgs-unstable
$ nix-channels --update
# Some very helpful errors
# ...
note: build failure may have been caused by lack of free disk space
Well… The nix module I showed you before said:
The generated image is sized to fit its contents, and a boot script automatically resizes the root partition to fit the device on the first boot.
Taking the SD card out and running lsblk
on my desktop computer showed me that the script never ran:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
...
sdc 8:32 1 119,1G 0 disk
├─sdc1 8:33 1 30M 0 part
└─sdc2 8:34 1 3,5G 0 part
So this turns out to be a problem within a problem.
No space available
Let’s then extend the partition. Still from my desktop computer:
$ sudo parted /dev/sdc
GNU Parted 3.6
Using /dev/sdc
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) print
Model: Generic MassStorageClass (scsi)
Disk /dev/sdc: 128GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
Number Start End Size Type File system Flags
1 8389kB 39,8MB 31,5MB primary fat16
2 39,8MB 3803MB 3763MB primary ext4 boot
(parted) resizepart 2 100%
(parted) quit
Let’s make the filesystem use the new space:
sudo resize2fs /dev/sdc2
Alright, seems good now:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
...
sdc 8:32 1 119,1G 0 disk
├─sdc1 8:33 1 30M 0 part
└─sdc2 8:34 1 119G 0 part
Deploying my configuration
We can finally install our channels and start downloading binaries from the nix cache.
I cloned my NixOS config in which I added a new host: dagon
.
Not enough RAM
Well, turned out my config was too heavy even though I didn’t set any HomeManager config on purpose.
The OOM Killer literally broke the system, it didn’t boot anymore, I just decided to start over at this point.
Swapfile
Reduced some parts of the config, added a swapfile, rewrote the image.
sudo fallocate -l 4G /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
To add it to your NixOS config:
swapDevices = [{ device = "/swapfile"; size = 4096; }];
Remote building
If you enabled the SSH server and rebuilt the default config (in /etc/nixos/
), you could remotely build your configuration over SSH from your desktop computer:
nixos-rebuild switch --flake .#HOSTNAME --target-host USERNAME@IP --use-remote-sudo
But note that if your computer is cross-compiling, you’ll have degraded build performance (but probably no RAM issue).
PAM fails, users get deleted, new install
In my config I had only one user : louis
.
But the image used a nixos
regular user and a root
one.
Building as nixos
broke everything, I believe both users got deleted during deployment, and I couldn’t use sudo
anymore.
Well, here goes a new image.
I added a root user and was finally able to build my config.
No Ethernet
So this issue appeared right after I deployed my config, I spent some time trying to fix it, but it ended fixing itself after some hours. I think that the MAC address was bound to a specific IP in my router from the previous system I had on it. I tinkered with it to set up some services and may have fixed it by accident.
Happy End
I successfully built my config and deployed a few services. Nextcloud was way easier to install with Nix. I also deployed Glance that I used to self-host on my machines before.