Installing Vanilla NixOS on a Raspberry Pi 4B with UEFI
In my last post, I installed NixOS on a Raspberry Pi 3B. I installed NixOS on an RPi 4B, this time using the Raspberry Pi UEFI firmware and a vanilla bootloader setup with systemd-boot instead of U-Boot, which is what’s included in the SD card images.
I’ll go through the process of installation step by step.
Update the EEPROM
The RPi 4 has an EEPROM containing the on-board bootloader code. That means that it can be updated – which isn’t the case for the older RPi’s like my RPi 3B. With recent enough versions of the EEPROM image, that on-board bootloader supports GPT partitioned SD cards. That’s great as we don’t have to put up with MBR shenanigans anymore. Note that those images are sadly closed-source.
I’m not exactly sure what “recent enough” means here (the release notes don’t mention adding GPT support anywhere), but if you run into problems during installation, you might want to try updating the EEPROM first.
I myself still had a working LibreELEC install on the Pi’s old SD card, which includes an EEPROM updater. With that I updated to the newest version before continuing just to be sure.
Preparing the SD Card
I started by partitioning my SD card exactly like I would an SSD for a normal installation on a PC, which means:
- A GPT partition table (
mktable gptin parted).
- An ESP partition for UEFI booting as first partition (a few hundred
MiB should be more than enough, although I tend to go a bit overboard
and make it a GiB when I have enough space on the device anyway)
mkpart primary 0% 1GiBin parted).
- Whatever other partitions we might want. Personally I use a single btrfs partition spanning the rest of the disk.
Raspberry Pi UEFI Firmware
When fiddling around with my RPi 3, I stumbled upon the great open-source Raspberry Pi UEFI firmware for the RPi 3 and for the RPi 4. For the RPi 3 I didn’t end up using that, because its on-board bootloader doesn’t support GPT disks and isn’t updatable like the RPi 4’s is. I described those problems in detail in that post.
Anyway, the RPi 4 does support GPT partitioned disks, and I loved the UEFI firmware when I tried it. It’s also available for the RPi 4, so I wanted to try it again. I downloaded the latest release and, following the instructions from there, I put the files on the ESP partition of the SD card. That was it, nothing more to do!
Just as before on the RPi 3, it just worked. Booting up the Pi now gives me a fully-fledged UEFI boot menu!
Note that the UEFI firmware does have some limitations due to its nature. E.g. as the RPi doesn’t have NVRAM, changes to UEFI variables from the OS are not persisted. Changes made from the boot menu are persisted, of course (due to the lack of NVRAM, on the SD card itself).
The installation itself is surprisingly easy with the UEFI firmware.
As I now had a normal boot menu supporting USB available, I could download the vanilla ARM ISO install image from the NixOS website and put it on a USB stick to boot from.
I could now install NixOS from a USB stick onto the SD card like I would on any normal PC, using an EFI-capable bootloader like systemd-boot (NixOS’s default IIRC) or GRUB. And – it just works! No need for weird Pi-specific boot stuff here. The only thing we have to be careful about is the hardware config – see the following section.
Other than on my RPi 3 before I had no problem using my NixOS flake
directly, it evaluated and built fine and in a reasonable timeframe. I
didn’t have to bother with copying over the system closure via
nix copy and SSH during the installation as detailed in the
RPi 3 article.
Note that copying over the system closure wouldn’t even work, because
while the RPi SD image has the Nix store on a plain ext4 root partition
on the card, the ISO install image has the store on an overlayfs with
very limited space. I actually tried to do
nix copy at
first, but ran out of space in the store. When you run
nixos-install, the derivations are downloaded/built
directly on the disk you’re installing onto, which circumvents the space
What I would love to be able to do is install NixOS onto the SD card
from my desktop, so I can then just put the SD card into the Pi, boot
it, and have it work with my config. However, the last time I tried
nixos-install didn’t seem to like being run from
another system arch. I might look into that again some time, though.
Maybe I can build SD card images with my config and an UEFI bootloader
like the pre-built NixOS RPi images, maybe with nixos-generators?
There are some caveats concerning the NixOS hardware config, though. Originally, I started off with the instructions from nix.dev, which uses the RPi 4 hardware config from the nixos-hardware repo. I included that in the config during installation – and it didn’t work.
I’m not the first one to run into that problem: there’s an issue in nixos-hardware already open. Seems to be a problem that started with NixOS 23.05.
The good thing is that the extra config from nixos-hardware doesn’t seem to be necessary at all anymore, according to a comment by Nebucatnetzer in the issue.
According to that comment, all we need are some additional kernel
boot.initrd.availableKernelModules = [ "usbhid" "usb_storage" "vc4" "pcie_brcmstb" # required for the pcie bus to work "reset-raspberrypi" # required for vl805 firmware to load ];
So I removed nixos-hardware again, added those modules to my config,
nixos-install – and it worked! The Pi boots and works
fine. I opened an issue for nix.dev
to improve the documentation there.
I have yet to try whether a graphical desktop, audio, video acceleration, etc. also work out of the box, I’ll update the article once I do.
nixos-generate-config doesn’t include those
initrd kernel modules. If they actually are required for the Pi to boot
and work, which I haven’t tried myself,
nixos-generate-config should detect that. Currently, it
only links to nixos-hardware. I’ll experiment a bit more with what
modules are required for what and then open an issue.
All in all: if the module detection issue is sorted out, we could do a NixOS installation following the normal NixOS installation instruction on a RPi using the UEFI firmware, which I think is really cool!
By the way, my current config for my RPi 4 can be found here: https://git.eisfunke.com/config/nixos/-/blob/main/devices/amethyst.nix