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:
- disko mounts the formatted partitions at
/mnt/boot, /mnt/nix, /mnt/persist.
nixos-install runs, completes.
umount -Rv /mnt is invoked.
- Modern
umount -R rejects /mnt because it isn't itself a mountpoint, returning exit 1, even though it could recurse into the mounted children.
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)
- 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.
- Bind-mount
$rootMountPoint to itself at the start of the install phase so it becomes a mountpoint, making umount -R happy.
- 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.
Summary
When building a disk image with
system.build.diskoImages(also reproducible viasystem.build.diskoImagesScript), the finalumount -Rv "$rootMountPoint"step inlib/make-disk-image.nixexits non-zero with:even though
nixos-installcompleted successfully (installation finished!). Because the wrapper script runs underset -efu, the non-zero exit kills the build VM's PID 1, producing a kernel panic:The resulting raw image is in fact complete and bootable, the failure is purely cosmetic cleanup, but the build derivation always reports failure.
Environment
github:nix-community/disko/master(commit65fb947964bd44fc0008faf77d1fcb7a9f40bb32)github:NixOS/nixpkgs/nixos-unstable(commitd233902339c02a9c334e7e593de68855ad26c4cb)mke2fs 1.47.3 (8-Jul-2025)Root cause (suspected)
umount -R <target>in recentutil-linuxrequires<target>to itself be a mountpoint and errors withnot mountedwhen it isn't, even when there are child mounts beneath it. Older versions silently no-op'd.In the disko config used,
/mntis never made a mountpoint, only its children (/mnt/boot,/mnt/nix,/mnt/persist) are. So:/mnt/boot,/mnt/nix,/mnt/persist.nixos-installruns, completes.umount -Rv /mntis invoked.umount -Rrejects/mntbecause it isn't itself a mountpoint, returning exit 1, even though it could recurse into the mounted children.set -eaborts the script. The VM's PID 1 dies. Kernel panics.Minimal reproducer
flake.nix:Run:
Observe:
installation finished!→umount: /mnt: not mounted→ kernel panic → build exits 1, butmain.rawis fully valid and boots fine.Relevant log tail
Suggested fixes (any of these)
lib/make-disk-image.nix:findmntto enumerate and unmount children explicitly.$rootMountPointto itself at the start of the install phase so it becomes a mountpoint, makingumount -Rhappy.Happy to send a PR, if it'd be useful.