RPi3 with UEFI?

Hi all,

I’ve had a pretty good experience with UEFI booting my Pi4 (ZFS root on USB SSD, initially grub now systemd-boot), so I decided to try it on my Pi3 as well, which is BTRFS root also on a USB SSD.

Using GitHub - pftf/RPi3: Raspberry Pi 3 UEFI Firmware Images, not having any luck.

Apparently the Pi3 bootloader requires MBR instead of GPT, and systemd-boot refuses to work with MBR.

I thought it would be no big deal – just use grub, but grub doesn’t seem to be producing any code in the EFI partition (I’m expecting something like efi/boot/bootaa64.efi).

When I run nixos-install, I get no errors, everything looks fine, just doesn’t boot. When I look closer, the boot*.efi file is missing.

When I nixos-enter into the image and manually NIXOS_INSTALL_BOOTLOADER=1 switch-to-configuration, I get the error below:

# NIXOS_INSTALL_BOOTLOADER=1 /run/current-system/bin/switch-to-configuration switch
updating GRUB 2 menu...
ERROR: Could not search B-tree: Function not implemented
Failed to retrieve subvolume info for /boot
Failed to install bootloader

BTRFS scrub comes up clean, the devices seems to mount and read normally.

I see that error string in btrfs-progs: btrfs-progs/libbtrfsutil/errors.c at a1926e39df13813ea4130f656a72b6052072848e · kdave/btrfs-progs · GitHub

Is anyone else using UEFI on a NixOS Pi3? If so, what’s your setup?

TIA!

My setup has:

  • systemd-boot
  • LUKS encrypted ext4 root
  • GPT with Hybrid MBR

I have a post on the general installation process:
https://prince213.top/blog/2025/01/21/nixos-rpi/
And this is my disk configuration with disko:
https://git.sr.ht/~prince213/dotfiles/tree/main/item/systems/pegasus/disk.nix

Did you make sure to set boot.loader.efi.canTouchEfiVariables = false; and boot.loader.grub.efiInstallAsRemovable = true;?

It also kinda sounds like your /boot partition isn’t mounted?

1 Like

That sounds like GRUB is trying to write to a btrfs-formatted /boot?

Edit: @ElvishJerricco sniped me :stuck_out_tongue:

Awesome, I will look into this!

I’d seen a post about a related setup: Booting a Raspberry Pi 3B with UEFI and a Hybrid MBR | Eisfunke

Yes (and also tried switching these around and back).

It is mounted. EDIT: (It is a top-level BTRFS subvolume.)

Yes – @boot is a top-level subvolume mounted to /boot, with a FAT partition at /boot/efi. I’ve used this setup on my Pi4 (and other BTRFS-root UEFI machines) without issue.

Also previously was working well via u-boot, as long as I compiled in the BTRFS (and ZSTD) bits: GitHub - n8henrie/nixos-btrfs-pi: Raspberry Pi SD card image for NixOS on BTRFS root

Stuff like this is exactly why I tell people to just use systemd-boot and to mount the ESP at /boot. It’s so much simpler and you don’t have to deal with grub screwing things up half the time.

I mentioned above – systemd-boot refuses to install due to being MBR, so includes some additional levels of complexity (figuring out GPT with Hybrid MBR). Unless you’re aware of a way to get systemd-boot to work with the standard RPi3 MBR setup?

A Hybrid MBR setup seems to be working well, thanks to @Prince213 for the suggestion.

I ended up following the guide at Booting a Raspberry Pi 3B with UEFI and a Hybrid MBR | Eisfunke for my setup, as it spelled things out a little more elaborately.

@Prince213 I have checked your blog post and it is a really nice guide, but how did you manage to run nixos-anywhere on the RPi3?

I am running into ‘out of space’ issues on the Raspberry Pi 3 (1GB RAM). The squashfs /nix/store overlay on tmpfs is just around 400MB, which seem to small, even with a minimal Raspberry Pi closure, to run nixos-anywhere.

Hi @qitta I’m sorry for the confusion. You don’t run nixos-anywhere on the Raspberry Pi, instead you run it on another machine, and nixos-anywhere will copy the closure directly to the newly formatted disk over SSH.

@Prince213 Thanks for your prompt response! I understand that nixos-anywhere needs to be run from another machine. The challenge I’m facing is that my minimal Raspberry Pi closure is approximately 800MB, whereas the available squashfs space after booting from the ISO is only around 400MB. As a result, any attempt to copy the 800MB closure to the Raspberry Pi – whether using nix-copy-closure or nixos-anywhere – ends up exhausting the squashfs/tmpfs.

For more context, the flake with your disko config (slightly adapted):

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    disko = {
      url = "github:nix-community/disko";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };
  outputs = { nixpkgs, disko, ... }: {
    nixosConfigurations = {
      raspberry = nixpkgs.lib.nixosSystem {
        system = "aarch64-linux";
        modules = [
          disko.nixosModules.disko
          ({ pkgs, modulesPath, lib, config, ... }: {
            imports = [
              (modulesPath + "/profiles/minimal.nix")
              (modulesPath + "/profiles/perlless.nix")
            ];

            boot.loader.systemd-boot.enable = true;
            boot.loader.efi.canTouchEfiVariables = true;

            networking.hostName = "raspberry";
            networking.interfaces."eth0".useDHCP = true;

            time.timeZone = "Europe/Berlin";
            environment.defaultPackages = [ ];
            environment.systemPackages = [ ];

            services.xserver.enable = false;
            services.openssh.enable = true;

            users.users.alice = {
              password = "secret";
              isNormalUser = true;
              extraGroups = [ "wheel" ];
            };

            disko.devices = {
              disk.main = {
                device = "/dev/mmcblk0";
                content = {
                  type = "gpt";
                  efiGptPartitionFirst = false;
                  partitions = {
                    boot = {
                      priority = 1;
                      type = "ef00";
                      size = "512M";
                      hybrid.mbrPartitionType = "0c";
                      content = {
                        type = "filesystem";
                        format = "vfat";
                        mountpoint = "/boot";
                        mountOptions = [ "umask=0077" ];
                      };
                    };
                    swap = {
                      priority = 2;
                      type = "8200";
                      size = "1G";
                      content = {
                        type = "swap";
                        randomEncryption = true;
                      };
                    };
                    root = {
                      priority = 3;
                      type = "8305";
                      size = "100%";
                      content = {
                        name = "root";
                        type = "luks";
                        content = {
                          type = "filesystem";
                          format = "ext4";
                          mountpoint = "/";
                        };
                      };
                    };
                  };
                };
              };
            };
            system.stateVersion = "23.11";
          })
        ];
      };
    };
  };
}

Flake build and size:

nix build .#nixosConfigurations.raspberry.config.system.build.toplevel -L
du -sch (nix-store -qR result) | awk '{ print $1 }' | tail -n 1
799M

Raspberry Pi 3 after bootup from iso:

ssh root@192.168.1.228 'df -h'

Filesystem      Size  Used Avail Use% Mounted on
devtmpfs         40M     0   40M   0% /dev
tmpfs           399M     0  399M   0% /dev/shm
tmpfs           200M  7.3M  193M   4% /run
tmpfs           399M   20M  380M   5% /
/dev/root        60G  1.2G   59G   2% /iso
/dev/loop0      1.1G  1.1G     0 100% /nix/.ro-store
tmpfs           399M  8.0K  399M   1% /nix/.rw-store
overlay         399M  8.0K  399M   1% /nix/store
[...]

Running nixos-anywhere with firmware in root folder gives me ‘out of space’ after some seconds:

 nix run github:nix-community/nixos-anywhere -- --flake .#raspberry --generate-hardware-config nixos-generate-config ./hardware-configuration.nix --extra-files root --target-host root@192.168.1.228

How did you solve this? Didn’t you run into the same issue? Is your blog post related to a Raspberry Pi 3 with 1GB RAM?

This is weird. I have a Raspberry Pi 3 Model B with 1 GB RAM and I wrote that blog post for this model.
I didn’t run into this issue, but I think the way nixos-anywhere does it is that it directly copies the closure to /mnt (the target file system) so it doesn’t require space on tmpfs or something like that.
Could you share detailed logs? Things like disko logs may help with debugging.

@Prince213 I just managed to install it using nixos-anywhere with the additional --no-disko-deps flag which only uploads the disko script but not the partitioning tools dependencies and is supposed to be run on low memory systems.