Skip to content

diskoImages: umount -Rv /mnt fails after successful install; PID 1 panic #1262

@codingCoffee

Description

@codingCoffee

Summary

When building a disk image with system.build.diskoImages (also reproducible via system.build.diskoImagesScript), the final umount -Rv "$rootMountPoint" step in lib/make-disk-image.nix exits non-zero with:

umount: /mnt: not mounted

even though nixos-install completed successfully (installation finished!). Because the wrapper script runs under set -efu, the non-zero exit kills the build VM's PID 1, producing a kernel panic:

Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100

The resulting raw image is in fact complete and bootable, the failure is purely cosmetic cleanup, but the build derivation always reports failure.

Environment

  • disko: github:nix-community/disko/master (commit 65fb947964bd44fc0008faf77d1fcb7a9f40bb32)
  • nixpkgs: github:NixOS/nixpkgs/nixos-unstable (commit d233902339c02a9c334e7e593de68855ad26c4cb)
  • Host: NixOS, x86_64-linux
  • util-linux in build VM: mke2fs 1.47.3 (8-Jul-2025)

Root cause (suspected)

umount -R <target> in recent util-linux requires <target> to itself be a mountpoint and errors with not mounted when it isn't, even when there are child mounts beneath it. Older versions silently no-op'd.

In the disko config used, /mnt is never made a mountpoint, only its children (/mnt/boot, /mnt/nix, /mnt/persist) are. So:

  1. disko mounts the formatted partitions at /mnt/boot, /mnt/nix, /mnt/persist.
  2. nixos-install runs, completes.
  3. umount -Rv /mnt is invoked.
  4. Modern umount -R rejects /mnt because it isn't itself a mountpoint, returning exit 1, even though it could recurse into the mounted children.
  5. set -e aborts the script. The VM's PID 1 dies. Kernel panics.

Minimal reproducer

flake.nix:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    disko = {
      url = "github:nix-community/disko/master";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, disko, ... }: {
    nixosConfigurations.repro = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        disko.nixosModules.disko
        ({ modulesPath, ... }: {
          imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];

          disko.devices.disk.main = {
            type = "disk";
            device = "/dev/vda";
            imageSize = "20G";
            content = {
              type = "gpt";
              partitions = {
                ESP = {
                  size = "512M";
                  type = "EF00";
                  content = {
                    type = "filesystem";
                    format = "vfat";
                    mountpoint = "/boot";
                    mountOptions = [ "umask=0077" ];
                  };
                };
                nix = {
                  size = "10G";
                  content = {
                    type = "filesystem";
                    format = "ext4";
                    mountpoint = "/nix";
                  };
                };
                persist = {
                  size = "100%";
                  content = {
                    type = "filesystem";
                    format = "ext4";
                    mountpoint = "/persist";
                  };
                };
              };
            };
          };

          # NOTE: no fileSystems."/" declared as ext4 on a partition, root is
          # tmpfs (or not declared); /mnt during install is never itself a
          # mountpoint, only its children are.
          fileSystems."/" = {
            device = "none";
            fsType = "tmpfs";
            options = [ "defaults" "size=2G" "mode=755" ];
          };

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

          users.users.demo = {
            isNormalUser = true;
            hashedPassword = "$6$xxxxx";  # any valid hash
          };
          users.allowNoPasswordLogin = true;

          system.stateVersion = "25.11";
        })
      ];
    };

    packages.x86_64-linux.image =
      self.nixosConfigurations.repro.config.system.build.diskoImagesScript;
  };
}

Run:

nix build .#image
QEMU_OPTS="-m 2048" ./result

Observe: installation finished!umount: /mnt: not mounted → kernel panic → build exits 1, but main.raw is fully valid and boots fine.

Relevant log tail

Random seed file /boot/loader/random-seed successfully written (32 bytes).
installation finished!
++ umount -Rv /mnt
umount: /mnt: not mounted
[   45.724680] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100
...
[   46.263791] Rebooting in 1 seconds..
Virtual machine didn't produce an exit code.

Suggested fixes (any of these)

  1. Tolerate the no-mountpoint case in lib/make-disk-image.nix:
    umount -Rv "$rootMountPoint" || mountpoint -q "$rootMountPoint" || true
    or use findmnt to enumerate and unmount children explicitly.
  2. Bind-mount $rootMountPoint to itself at the start of the install phase so it becomes a mountpoint, making umount -R happy.
  3. Drop the trailing umount entirely the VM is about to power off; the loop device is host-side and gets detached at VM shutdown.

Happy to send a PR, if it'd be useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions