NixOS on ARM/Raspberry Pi 5
The Raspberry Pi family of devices is a series of single-board computers made by the Raspberry Pi Foundation. They are all based on Broadcom System-on-a-chip (SoCs).
Current Status
NixOS is not officially supported on the Raspberry Pi 5, and the community efforts to bring the operating system to the development board have been less successful than the ones targeting the previous generation, the Raspberry Pi 4. It is advisable, for the time being, that all critical projects continue using and deploying the older model, for which the support is significantly more robust.
For the adventurous, there are multiple community efforts enabling NixOS on the board, with various degrees of user friendliness, support availability or robustness, some of which can be found in the Other solutions section below. Amongst them, the community has flagged one repository that seems to have achieved a reasonable trade-off between all the necessary requirements for such a project; this is described in the Proposed Solution section below. While by no means official, and its level of support still under development, it is a good enough starting ground for people wanting to take advantage of their Raspberry Pi 5.
However, this solution reuses proprietary software distributed by the Raspberry Pi Foundation; which is highly criticized by the NixOS community, and the Linux community at large, for reasons including reproducibility, transparency, security, predictibility, etc. There are efforts in the community to address these issues as well, and the following sub-section is an overview into these efforts.
Generic UEFI boot support
NixOS doesn't run out-of-box, but relies on several tweaks on the boot process that are maintained by different individuals and spread over multiple repositories. The Raspberry Pi 5's boot process follows the typical boot stages on embedded devices, and has the following boot loader steps by default:
The pain points for NixOS support are the Pi's custom EEPROM boot bootloader, its proprietary, closed-source firmware (code to use to hardware components) and its separately maintained Linux kernel, all of which we would need to update, build and test constantly and separately from the other NixOS Linux kernel variations, which is a large, unmaintainable burden for the NixOS community when the Pi 5 is not the only supported SoC.
The more sustainable goal would be to move towards UEFI support, which would mimic how desktop computers boot into the NixOS operating system, thus minimising the amount of bespoke maintenance needed for the development board. This is what this process would look like:
The ROM and EEPROM can't be modified, as they are built into the hardware on the development board. However, UEFI is also used for booting normal Intel/AMD computers, and the systemd-boot boot loader is THE software that allows us to have and select from multiple NixOS generations on boot (and perform rollbacks if we messed up). Further details about the steps involved in achieving this can be found in the Other Solutions section, under Generic UEFI Boot.
Finally, there should also be U-Boot support soon, as most development boards are widely supported by the project. In that case, the boot process would look like:
Proposed Solution
There is a repository aiming to develop a declarative way of defining Raspberry Pi setups, including, but not limited to, the Raspberry Pi 5. The project is called nixos-raspberrypi, and so far seems to be successful at achieving functioning system builds. It also provides a binary cache, speeding up iteration and deployment.
Take note that this solution reuses the proprietary firmware distributed by The Raspberry Pi Foundation. If that's not desired, one of the Other Solutions may be preferable.
Setting up a NixOS system on your Raspberry Pi 5 consists of a number of steps. First, it's important to get a SD Image installer onto the device. If you're not currently running on an ARM system (almost definitely the case), you have three options:
- Cross compile the image (Note: an editor should add how-to information);
- Hope that the compiled image is available in the cache;
- Bootstrap a build machine onto the Raspberry Pi 5.
Step 1: Building the SD Image
The project provides build images as well, which are modified versions of the nix-hardware ones. Before getting into building one, in order to make use of the community binary caches, and potentially avoid rebuilding the image, make sure you are added to the trusted users in your own nix configuration:
trusted-users = username
Or, alternatively, in your nixos configuration:
{
nix.settings.trusted-users = [ "your-username" ];
}
Afterwards, you can build one of the SD images:
$ nix build github:nvmd/nixos-raspberrypi#installerImages.rpi5
You can either cross-compile the image on your system, or alternatively, you can install the "normal" Raspberry Pi OS on your Raspberry Pi 5, then install Nix standalone (multi-user), and set it up as a distributed build machine.
Finally, simply copy the image to the current directory from the result
directory, extract it and write it to the SD card. You can find more information on the NixOS on ARM/Installation page.
Step 2: Installing NixOS
Once you're running an installer image, you can create a flake referencing the repository. For example:
{
description = "Example Raspberry Pi 5 configuration flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
nixos-raspberrypi.url = "github:nvmd/nixos-raspberrypi/main";
};
nixConfig = {
extra-substituters = [
"https://nixos-raspberrypi.cachix.org"
];
extra-trusted-public-keys = [
"nixos-raspberrypi.cachix.org-1:4iMO9LXa8BqhU+Rpg6LQKiGa2lsNh/j2oiYLNOQ5sPI="
];
};
outputs = { self, nixpkgs, nixos-raspberrypi }@inputs:
{
nixosConfigurations = {
yourHostname = nixos-raspberrypi.lib.nixosSystem {
specialArgs = inputs;
modules = [
({...}: {
imports = with nixos-raspberrypi.nixosModules; [
raspberry-pi-5.base
raspberry-pi-5.bluetooth
];
})
({ ... }: {
networking.hostName = "yourHostName";
users.users.yourUserName = {
initialPassword = "yourInitialPassword";
isNormalUser = true;
extraGroups = [
"wheel"
];
};
services.openssh.enable = true;
})
({ ... }: {
fileSystems = {
"/boot/firmware" = {
device = "/dev/disk/by-uuid/2175-794E";
fsType = "vfat";
options = [
"noatime"
"noauto"
"x-systemd.automount"
"x-systemd.idle-timeout=1min"
];
};
"/" = {
device = "/dev/disk/by-uuid/44444444-4444-4444-8888-888888888888";
fsType = "ext4";
options = [ "noatime" ];
};
};
})
];
};
};
};
}
Finally, simply nixos-rebuild switch --flake .#yourHostname
the flake.
Other Solutions
In general, as show in in the first section, the closed source and proprietary software powering part of the Raspberry Pi development boards is a big source of contention. Therefore, there are largely two kinds of solutions going forward: moving towards a generic boot process, and using proprietary software, each with advantages and disadvantages.
Using proprietary software
Besides the current Proposed Solution, there has been one more attempt at https://github.com/tstat/raspberry-pi-nix (just follow the example and hints at https://github.com/tstat/raspberry-pi-nix/issues/13) and has the best out-of-the-box experience. You have to remote-build many Nix packages, probably the kernel as well, yourself (e.g. using the Pi without NixOS as an intermediate remote builder) and that can take several hours though.
Generic UEFI Boot
This solution is technically more complex than the others, and the overview is separated into sections.
Raspberry Pi Boot stages
To understand the adaptions for NixOS better, it's helpful to understand more about the stages:
1. ROM boot loader: The first-stage boot loader comes "burned in" on the Pi in a tiny One-Time-Programmable memory (OTP) so it cannot be changed anymore. It's only able to load the next second-stage boot loader below, and reset it in case you have messed up.
See the official documentation.
Nothing to adapt here.
2. EEPROM boot loader: The second-stage boot loader comes built-in on the Pi in a larger, rewriteable
EEPROM.
This loader is also very limited and is only able to search for and start yet another, third-stage boot loader from other
storage hardware like an SD card, an NVMe SSD, a USB disk, or from the network.
This loader (like many second-stage boot loaders of other devices) is so size-constrained that it only contains
the bare minimum code to be able to read from an FAT formatted partition. That's why you see and want a separate small
/boot
partition on your SD card or SSD that is formatted "FAT" or "VFAT", while your main data is stored on a second
"rootfs" or /
partition with fancy, newer partition types like "ext4", "ZFS" or "btrfs".
See the official documentation.
See EEPROM image releases for improved and wider hardware support.
This boot loader can be updated via the rpi-eeprom-update
terminal tool
(also available in Nixpkgs)
and loads the binary images (the firmware-2712/pieeprom-*.bin
files) from the
rpi-eeprom GitHub project.
Nothing to adapt here yet. However, there's a feature request to support smaller third-stage boot loaders in this second-stage.
3. Firmware boot loader: The third-stage boot loader is loaded from the first partition (usually called /boot
)
of an SD card, NVME SSD or other storage hardware described above.
Because size is usually not an issue here, you can have large, fully-fledged boot loaders like
systemd-boot (default with NixOS; requires UEFI), or full
U-Boot (popular with embedded devices like the Pi) or
GRUB (generally popular among Linux distros).
However, the standard Pi 5 setup has no third-stage boot loader.
The second stage EEPROM boot loader loads the firmware (code to control other hardware on the Pi 5; device tree files
in compact binary format *.dtb
), some settings (cmdline.txt
for kernel settings,
config.txt
for firmware settings),
and the Linux kernel itself from a /boot/firmware/
folder. On the Pi 5's default Debian image this is
the kernel2712.img
(specialized, more-performant kernel
named after the Pi 5's Brodcom BCM2712 ARMv8 SoC chip) or as a fallback the kernel8.img
(generic, slower ARMv8 kernel for the Pi 4 that
also works for Pi 5) that you find on the Pi firmware GitHub project.
See the official documentation.
Setting up a generic UEFI NixOS
The task to get a generic NixOS setup requires a
- UEFI boot loader for Pi 5: There exists a WIP EDK2 for Pi 5 Github project but with a few limitations see the project for details.
- systemd-boot boot loader: Works
- generic Linux kernel that works for the Pi 5's ARM v8 processor and hardware: An almost generic Pi 5 compatible kernel exists at the NixOS-hardware repository; it's an adaption from a kernel for the Pi 4)
1. ROM -> 2. EEPROM -> 3. UEFI boot loader (EDK2) -> 4. systemd-boot boot loader -> Pi 4/5-adapted NixOS Kernel -> NixOS
1. Install EDK2 (UEFI firmware implementation):
We need the first partition of the SD card (or NVMe SSD, etc.) again to be formatted as FAT
but marked as an ESP (EFI System Partition) to conform to (U)EFI standards.
In this partition we need to place the EDK2 firmware file RPI_EFI.fd
and a config.txt
file with a line armstub=RPI_EFI.fd
which instructs the EEPROM boot loader to load EDK2 instead of a Linux loader stub.
See the EDK2 for Pi 5 Github project; the releases already contain both of these files.
See a guide on how to setup partitions and these files.
2. Install systemd-boot, kernel and NixOS:
The rest is a usual NixOS installation on a second partition with the caveat to select a Linux kernel that supports the Pi 5 like the Pi 5 compatible Linux kernel is available in nixos-hardware.
Follow this guide to build
a NixOS system closure that you can install manually onto the Pi with a nixos-install
call.
That install tool will install the systemd-boot loader at /boot/EFI/systemd/systemd-bootaa64.efi
and the kernel files at /boot/EFI/nixos/*.efi
onto your first ESP partition
and the rest of the NixOS system into your second partition.
Alternative board-specific installation notes
First, install EDK2, following the instructions from the port README. With EDK2 installed as the Platform Firmware, you can follow the standard instructions for UEFI on ARM.
EDK2 enables booting a mainline kernel, but hardware support will be very limited. Notably, you'll need to perform the installation using Wi-Fi, as Ethernet is unsupported. Once the system is installed, you can switch to the vendor's modified kernel. This is not (yet?) available in Nixpkgs, so you'll need to get it from a flake. If you're not using flakes, you can simply add this to your configuration:
{
boot.kernelPackages = (import (builtins.fetchTarball https://gitlab.com/vriska/nix-rpi5/-/archive/main.tar.gz)).legacyPackages.aarch64-linux.linuxPackages_rpi5;
}
For the vendor kernel to boot properly, you must switch from ACPI to Device Tree in the UEFI settings (at Device Manager → Raspberry Pi Configuration → ACPI / Device Tree → System Table Mode). When using the vendor kernel (which provides full power management support), you may additionally wish to remove force_turbo=1
from /boot/config.txt
.
If you are using nixos-unstable, then you can also use the rpi4 kernel (which is a generic aarch64 kernel for Pi 3 and later models). Although, due to a smaller page size, this will have slightly worse performance:
{
boot.kernelPackages = pkgs.linuxPackages_rpi4;
}
Troubleshooting
GPU
For the GPU drivers to work, dtoverlay=vc4-kms-v3d-pi5
must be added to /boot/config.txt
, and the vendor kernel must currently be used. Only Wayland-based compositors are supported without additional configuration (see the nixos-hardware PR linked previously). Note that Xwayland applications may produce broken graphics on KDE; the root cause of this issue has not yet been evaluated.
The rpi5-uefi download does not include overlays. You can get them by copying the boot/overlays
folder from the firmware repository to /boot
(so that /boot/overlays/vc4-kms-v3d-pi5.dtbo
is available).
Bluetooth
If your Bluetooth doesn't show up, and you are getting errors in dmesg regarding the serial port at 107050c00, add the following to your NixOS configuration:
{
boot.kernelParams = [ "8250.nr_uarts=11" "console=ttyAMA10,9600" "console=tty0"];
}
Using the Pi 5 as a remote builder to build native ARM packages for the Pi 5
Building an NixOS system image that can be flashed to an SD card or NVMe SSD requires to build ARM binaries, more specifically for the "aarch64-linux"
platform. From a typical Intel/AMD computer we can either
- emulated native compile using QEMU virtualization by enabling the binfmt kernel feature on NixOS configuration setting
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
). This can be fast if everything is downloaded pre-compiled from the cache.nixos.org cache and only few packages really need local compilation. In reality it can be extremely slow, e.g. compiling a Linux kernel alone can take days. - cross-compile to ARM using as to happen natively, but nothing will be cached from cache.nixos.org as this is not pre-build. So the compile itself is fast but there will be a lot more to compile locally. In practice it's quite fragile, because you may encounter packages that don't really support cross-compilation get stuck.
- native compile on an remote builder like the Pi 5 itself running its custom Debian Linux at the beginning or later NixOS. This is quite simple to setup and reasonably fast as most packages are pre-build and cached on cache.nixos.org, and building a remaining Linux kernel only takes 2-3h on the Pi 5.
Setting up the Pi 5 as a remote native builder can be done following the steps at https://wiki.nixos.org/wiki/Distributed_build. The rough steps are as follows:
- Install the Nix package manager on Pi 5 Debian OS the normal, multi-user way with
sh <(curl -L https://nixos.org/nix/install) --daemon
. If you already have NixOS running on the Pi 5, then you can skip this step. - Setup a
ssh
connection from your local machine to the Pi, especially addingSetEnv PATH=/nix/var/nix/profiles/default/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
to the Pi's/etc/ssh/sshd_config
file. If you already have NixOS running on the Pi 5, then you can skip this step. - Make the remote Pi known to you local computer by adding it as a
nix.buildMachines
entry to your/etc/nix/configuration.nix
file and use connection protocolssh-ng
(!). - You can then build, e.g. an NixOS sd card image with a call similar to
nix build .\#nixosConfigurations.pi5.config.system.build.sdImage
- flash that resulting image onto an SD card or NVMe SSD using a call similar to
zstdcat result/sd-image/nixos-sd-image-23.11.20230703.ea4c80b-aarch64-linux.img.zst | sudo dd of=/dev/mmcblk0 bs=100M status=progress
and place that card into the Pi 5.
Missing:
- How to do cross-compilation. ( and/or explain why the above emulation is required at all)
Deploy and Update the Pi 5 NixOS system once it's running NixOS
Once the Pi 5 is running NixOS, you can update it with newer NixOS system configurations using e.g. the usual nix-rebuild
tool with a call similar to
nixos-rebuild --flake .#pi5 --build-host piuser@pi5 --target-host piuser@pi5 --use-remote-sudo switch
that uses the SSH connection from the remote builder section.
See this guide for a good explanation of this terminal call.