Installing GRUB without running VM (make-disk-image)

I currently use nixos-generators (e.g. make-disk-image.nix) to generate disk images from NixOS configurations.

This process is very slow (15 minutes+ is typical). This is probably exacerbated by my configuration (M1 Mac running a linux-builder VM, so we have qemu in qemu!) but even when running natively I’ve had trouble.

I’ve noted that actually building the initial disk image is very fast, but that an expensive bit is then installing GRUB onto the disk (and otherwise activating NixOS, but I don’t care so much about that). This is because it boots up a VM with QEMU in order to do this.

I’m wondering if there’s any clean way to get the boot partition set up without needing to actually run a VM. This is clearly possible as it happens during nixos-install itself.

This seems like it might be hard, e.g. nixos-install (which seems like the usual not-on-running-system) mechanism to install the bootloader actually runs the script, I assume bind-mounting the /boot dir into its correct location.

However, I’m wondering if folks had ideas. Some off top of my own head:

I’m looking for help on: Which of these two seems more viable, any roadblocks I’ll hit, any other ideas?

In the end I was able to figure this out. You can do something like

    createEspFolderUnsandboxed = configuration: pkgs.writeShellApplication {
        runtimeInputs = [
            pkgs.findutils
            pkgs.nix
            configuration.config.system.build.toplevel
            (pkgs.systemd.overrideAttrs (old: {
                patches = (old.patches or []) ++ [ ./systemd.patch ];
            })) ];
        name = "createEspFolder";
        text = ''
        # this section inspired by install-nixos
        mkdir /etc
        mkdir /tmp
        export TMPDIR=/tmp

        touch /etc/NIXOS

        export SYSTEMD_RELAX_ESP_CHECKS=1
        export SYSTEMD_RELAX_XBOOTLDR_CHECKS=1
        bootctl install --no-variables --graceful --esp-path=/boot --entry-token="literal:temporary"

        # nix creates bootloader entries for profiles. So let's create one.
        mkdir -p /nix/var/nix/profiles
        ln -s ${configuration.config.system.build.toplevel} /nix/var/nix/profiles/system-1-link
        ln -s system-1-link /nix/var/nix/profiles/system

        switch-to-configuration boot
        echo "Switched to configuration"
        '';
    };
    createEspFolder = configuration: pkgs.runCommand "createEspFolder" {
        buildInputs = [ pkgs.bubblewrap (createEspFolderUnsandboxed configuration) ];
    } ''
        # this segment largely inspired by buildFHSEnv
        mkdir $out
        cmd=(
            bwrap
            --die-with-parent
            --proc /proc
            --bind /nix/store /nix/store
            --bind $out /boot
            createEspFolder
        )
        exec "''${cmd[@]}"
        '';

provided that you’ve done something like this: Allow `bootctl install --esp-path=/efi --boot-path=/boot` to skip checking if /efi is the root of it's filesystem · Issue #29871 · systemd/systemd · GitHub. Note that it requires systemd boot. Grub seems to access more exciting files.