Options to install and configure memtest86plus seem inconsistent and broken

boot.loader.grub.memtest86.enable = true; creates this entry in grub.cfg:

menuentry "Memtest86+" {
  linux ($drive1)/boot/memtest.bin 
}

But what is the proper way of getting memtest.bin into that location?
The docs actually provide an example of installing memtest.bin using extraFiles like I have below, but that puts it in /boot/efi/memtest.bin, not /boot/memtest.bin
Is this a bug, or am I missing something? Does boot.loader.grub.memtest86.enable need to be fixed to use linux ($drive1)/boot/efi/memtest.bin?

  boot.loader.grub = {
    enable = true;
    extraEntriesBeforeNixOS = false;
    default = "saved";              # remember last booted entry
    devices = [ "nodev" ];          # wiki says it is ignored with EFI, but we need to set it to some value
    efiSupport = true;
    efiInstallAsRemovable = true;   # helpful for portability?
    useOSProber = true;             # generate grub entry for existing OS (I had Windows on another disk with an MBR bootloader, so before this would work I had to convert that disk to GPT partition table and EFI boot loader, using Microsoft mbr2gpt - see https://answers.microsoft.com/en-us/windows/forum/all/convert-an-existing-windows-10-installation-from/aa8c2de3-460b-4a8c-b30b-641405f800d7) 
    splashImage = null;             # see https://discourse.nixos.org/t/how-to-apply-the-breeze-grub-theme/4341/6
    #theme = "${pkgs.kdePackages.breeze-grub}/grub/themes/breeze"; # not good
    memtest86.enable = true; # package needs to be installed separately
    extraFiles = { "memtest.bin" = "${pkgs.memtest86plus}/memtest.bin"; };
    extraEntries = ''
      menuentry "Reboot" --class reboot {
        reboot
      }
      menuentry "Reboot to UEFI setup (~Bios)"  --class bios {
        fwsetup
      }
      menuentry "Poweroff" --class shutdown {
        halt
      }
    '';

Thanks.

Ah, I also have boot.loader.efi.efiSysMountPoint = "/boot/efi";, so it seems that is causing the mixup.
Trying to change it to /boot causes other problems though.
If I leave /boot/efi mounted:

/nix/store/4spmk51bavzfyh7s9rz0vv0kv8lq7jdc-grub-2.12/sbin/grub-install: error: /boot doesn't look like an EFI partition.
/nix/store/qw78m8w2cid0c1kf2l47kryvh1m9w18k-install-grub.pl: installation of GRUB EFI into /boot failed: Inappropriate ioctl for device
Failed to install bootloader

If I unmount it and remount to /boot first:

> Adding configuration to bootloader
updating GRUB 2 menu...
cannot copy /nix/store/xlp1f8xyz69q5dc4yc6qgblf74kwvdb3-initrd-linux-6.6.58/initrd to /boot/kernels/xlp1f8xyz69q5dc4yc6qgblf74kwvdb3-initrd-linux-6.6.58-initrd.tmp: No space left on device
Failed to install bootloader

The implementation of that option is quite short:

config = mkIf cfg.enable {
  boot.loader.grub.extraEntries = ''
    menuentry "Memtest86+" {
      linux @bootRoot@/memtest.bin ${toString cfg.params}
    }
  '';
  boot.loader.grub.extraFiles."memtest.bin" = "${memtest86}/memtest.bin";
};

(cfg is config.boot.loader.grub.memtest86, and memtest86 is pkgs.memtest86plus.)

So it seems like it should be putting memtest.bin somewhere already, without you needing to touch boot.loader.grub.extraFiles, fwiw. (Whether that location is correct, I don’t know.)

Ah, it is actually that implementation which errors, because I still get the error after removing my use of extraFiles.

I’ve also configured boot.loader.efi.efiSysMountPoint = "/boot/efi"; because I followed this guide for encrypted /boot on NixOS.

After killing a couple watt-years I managed to coerce a working solution out of an LLM, based on the original implementation in the nixpkgs memtest module:

boot.loader.efi.efiSysMountPoint = "/boot/efi";

boot.loader.grub = {
  # existing
  enable = true;
  device = "nodev";
  efiSupport = true;
  enableCryptodisk = true;
  configurationLimit = 15;

  # don't enable this
  memtest86.enable = false;

  # new thing
  extraFiles."memtest.bin" = pkgs.memtest86plus.efi;
  extraEntries = ''
    menuentry "Memtest86+" {
      # -- CAUTION: copied clanker code & comments --
      # Find the partition with the file and set it as root
      # This switches $root from the LVM to the EFI partition
      search --set=root --file /memtest.bin

      # Load the binary.
      # Note: chainloader is technically correct for .efi binaries. 
      # The 'linux' command is legacy behavior for this tool and often fails on strict UEFI.
      chainloader /memtest.bin
    }
  '';
};
(Additional clanker commentary slop in case anyone finds it helpful)

The core issue is a mismatch between how Linux sees your filesystem and how GRUB sees it when efiSysMountPoint is used with an encrypted root.

The Problem: The “Ghost” Directory

  1. In Linux (OS running): The kernel mounts your EFI partition (nvme0n1p1) to /boot/efi. When you list that directory, you see the contents of the partition.
  2. In GRUB (Bootloader running): GRUB has decrypted your LVM (/ssd-root) and set it as $root. It sees the directory /boot/efi that exists on your encrypted drive, but it is empty. GRUB does not automatically “mount” the EFI partition into that directory like Linux does.

Your memtest.bin is physically sitting on nvme0n1p1. GRUB is looking at the encrypted LVM.

The Solution

You need to tell GRUB to search for the partition containing the file and load it from there, rather than looking on the default encrypted root.

Here is the corrected configuration. I recommend using chainloader because memtest86plus.efi is a native PE/COFF binary; forcing it to load via the linux command (as the upstream module tries to do) is often flaky on pure UEFI setups and contributes to the errors you are seeing.

– snip – (repeated config from above)

Explanation of Changes

  1. extraFiles."memtest.bin": Since your EFI mount point is /boot/efi, NixOS copies this file to /boot/efi/memtest.bin in your filesystem. On the actual FAT32 partition, this file is simply at the root: /memtest.bin.
  2. search --set=root --file /memtest.bin: This command scans your visible partitions (including the unencrypted EFI partition) for that specific file. Once found, it changes GRUB’s $root variable to that partition temporarily.
  3. chainloader /memtest.bin: Now that $root is pointing to the EFI partition, we can load the file directly.

Why not use linux command?

The upstream NixOS module uses linux @bootRoot@/memtest.bin because it attempts to support both BIOS (which uses a Linux-kernel-like header) and UEFI with a single config. However, on a pure UEFI system, memtest86plus.efi is an EFI application. While some versions support the Linux handoff protocol, chainloader is the native mechanism for executing EFI binaries and is significantly more reliable.


Works on my machine!™