From a75b9ddff0ae23b24ee7f1eba3ae55cf1734e525 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Sun, 4 Jan 2026 15:00:42 +0100 Subject: [PATCH 1/4] nixos/tests/repart: use image.filePath instead of deprecated imageFile --- nixos/tests/appliance-repart-image-verity-store.nix | 2 +- nixos/tests/appliance-repart-image.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nixos/tests/appliance-repart-image-verity-store.nix b/nixos/tests/appliance-repart-image-verity-store.nix index 09a0c95703ed..9339ac9a401a 100644 --- a/nixos/tests/appliance-repart-image-verity-store.nix +++ b/nixos/tests/appliance-repart-image-verity-store.nix @@ -101,7 +101,7 @@ "-f", "qcow2", "-b", - "${nodes.machine.system.build.finalImage}/${nodes.machine.image.repart.imageFile}", + "${nodes.machine.system.build.finalImage}/${nodes.machine.image.filePath}", "-F", "raw", tmp_disk_image.name, diff --git a/nixos/tests/appliance-repart-image.nix b/nixos/tests/appliance-repart-image.nix index edad11043a84..bba163b463e4 100644 --- a/nixos/tests/appliance-repart-image.nix +++ b/nixos/tests/appliance-repart-image.nix @@ -109,7 +109,7 @@ in "-f", "qcow2", "-b", - "${nodes.machine.system.build.image}/${nodes.machine.image.repart.imageFile}", + "${nodes.machine.system.build.image}/${nodes.machine.image.filePath}", "-F", "raw", tmp_disk_image.name, From dff30f9b791cdaba5c651a10879f821122ef0e51 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Sun, 4 Jan 2026 15:06:17 +0100 Subject: [PATCH 2/4] nixos/image/repart-verity-store: rename system.build.finalImage to system.build.image --- nixos/modules/image/repart-verity-store.nix | 10 +-- nixos/modules/image/repart.nix | 71 +++++++++++-------- .../appliance-repart-image-verity-store.nix | 2 +- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/nixos/modules/image/repart-verity-store.nix b/nixos/modules/image/repart-verity-store.nix index b0b137789bec..c18099b37bf6 100644 --- a/nixos/modules/image/repart-verity-store.nix +++ b/nixos/modules/image/repart-verity-store.nix @@ -112,10 +112,11 @@ in }; system.build = { + finalImage = lib.warn "system.build.finalImage has been renamed to system.build.image" config.system.build.image; # intermediate system image without ESP intermediateImage = - (config.system.build.image.override { + (config.image.repart.image.override { # always disable compression for the intermediate image compression.enable = false; }).overrideAttrs @@ -162,8 +163,8 @@ in ); # final system image that is created from the intermediate image by injecting the UKI from above - finalImage = - (config.system.build.image.override { + image = lib.mkOverride 99 ( + (config.image.repart.image.override { # continue building with existing intermediate image createEmpty = false; }).overrideAttrs @@ -216,7 +217,8 @@ in rm -v repart-output_orig.json ''; } - ); + ) + ); }; }; diff --git a/nixos/modules/image/repart.nix b/nixos/modules/image/repart.nix index 54f07eb8c48a..cfa90fd5f512 100644 --- a/nixos/modules/image/repart.nix +++ b/nixos/modules/image/repart.nix @@ -282,6 +282,15 @@ in ''; }; + image = lib.mkOption { + type = lib.types.package; + internal = true; + readOnly = true; + description = '' + The image built by this module. Used as the default for `system.build.image`. + ''; + }; + assertions = lib.mkOption { type = options.assertions.type; default = [ ]; @@ -356,6 +365,37 @@ in finalPartitions = lib.mapAttrs addClosure cfg.partitions; + image = + let + fileSystems = lib.filter (f: f != null) ( + lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions + ); + + format = pkgs.formats.ini { listsAsDuplicateKeys = true; }; + + definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format ( + lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions + ); + + mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions; + val = pkgs.callPackage ./repart-image.nix { + systemd = cfg.package; + inherit (config.image) baseName; + inherit (cfg) + name + version + compression + split + seed + imageSize + sectorSize + finalPartitions + ; + inherit fileSystems definitionsDirectory mkfsEnv; + }; + in + lib.asserts.checkAssertWarn cfg.assertions cfg.warnings val; + assertions = lib.mapAttrsToList ( fileName: partitionConfig: let @@ -401,36 +441,7 @@ in ); }; - system.build.image = - let - fileSystems = lib.filter (f: f != null) ( - lib.mapAttrsToList (_n: v: v.repartConfig.Format or null) cfg.partitions - ); - - format = pkgs.formats.ini { listsAsDuplicateKeys = true; }; - - definitionsDirectory = utils.systemdUtils.lib.definitions "repart.d" format ( - lib.mapAttrs (_n: v: { Partition = v.repartConfig; }) cfg.finalPartitions - ); - - mkfsEnv = mkfsOptionsToEnv cfg.mkfsOptions; - val = pkgs.callPackage ./repart-image.nix { - systemd = cfg.package; - inherit (config.image) baseName; - inherit (cfg) - name - version - compression - split - seed - imageSize - sectorSize - finalPartitions - ; - inherit fileSystems definitionsDirectory mkfsEnv; - }; - in - lib.asserts.checkAssertWarn cfg.assertions cfg.warnings val; + system.build.image = cfg.image; }; meta.maintainers = with lib.maintainers; [ diff --git a/nixos/tests/appliance-repart-image-verity-store.nix b/nixos/tests/appliance-repart-image-verity-store.nix index 9339ac9a401a..5751cec44bc2 100644 --- a/nixos/tests/appliance-repart-image-verity-store.nix +++ b/nixos/tests/appliance-repart-image-verity-store.nix @@ -101,7 +101,7 @@ "-f", "qcow2", "-b", - "${nodes.machine.system.build.finalImage}/${nodes.machine.image.filePath}", + "${nodes.machine.system.build.image}/${nodes.machine.image.filePath}", "-F", "raw", tmp_disk_image.name, From a5c1fff2497b698a4bd06f582bdfd625c9c0d4d6 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Wed, 7 Jan 2026 11:29:28 +0100 Subject: [PATCH 3/4] nixos/image/repart-verity-store: also support building images that do not have system.image.version set The current limitation seems artificial and gives a very confusing error message --- nixos/modules/image/repart-verity-store.nix | 6 +- .../appliance-repart-image-verity-store.nix | 82 +++++++++++-------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/nixos/modules/image/repart-verity-store.nix b/nixos/modules/image/repart-verity-store.nix index c18099b37bf6..b396c445c617 100644 --- a/nixos/modules/image/repart-verity-store.nix +++ b/nixos/modules/image/repart-verity-store.nix @@ -123,7 +123,11 @@ in ( _: previousAttrs: { # make it easier to identify the intermediate image in build logs - pname = "${previousAttrs.pname}-intermediate"; + name = + if previousAttrs ? pname then + "${previousAttrs.pname}-${previousAttrs.version}-intermediate" + else + "${previousAttrs.name}-intermediate"; # do not prepare the ESP, this is done in the final image systemdRepartFlags = previousAttrs.systemdRepartFlags ++ [ "--defer-partitions=esp" ]; diff --git a/nixos/tests/appliance-repart-image-verity-store.nix b/nixos/tests/appliance-repart-image-verity-store.nix index 5751cec44bc2..2199ab368f1f 100644 --- a/nixos/tests/appliance-repart-image-verity-store.nix +++ b/nixos/tests/appliance-repart-image-verity-store.nix @@ -10,13 +10,8 @@ willibutz ]; - nodes.machine = - { - config, - lib, - pkgs, - ... - }: + defaults = + { config, lib, ... }: let inherit (config.image.repart.verityStore) partitionIds; in @@ -75,10 +70,7 @@ initrd.systemd.enable = true; }; - system.image = { - id = "nixos-appliance"; - version = "1"; - }; + system.image.id = "nixos-appliance"; # don't create /usr/bin/env # this would require some extra work on read-only /usr @@ -86,6 +78,12 @@ system.activationScripts.usrbinenv = lib.mkForce ""; }; + nodes.machine = { + system.image.version = "1"; + }; + + nodes.without-version = { }; + testScript = { nodes, ... }: # python '' @@ -93,32 +91,50 @@ import subprocess import tempfile - tmp_disk_image = tempfile.NamedTemporaryFile() + def create_disk_image(qemu_img, backing_file): + tmp = tempfile.NamedTemporaryFile() + subprocess.run([ + qemu_img, + "create", + "-f", + "qcow2", + "-b", + backing_file, + "-F", + "raw", + tmp.name, + ], check=True) + return tmp - subprocess.run([ + def run_verity_tests(machine): + with subtest("Running with volatile root"): + machine.succeed("findmnt --kernel --type tmpfs /") + + with subtest("/nix/store is backed by dm-verity protected fs"): + verity_info = machine.succeed("dmsetup info --target verity usr") + assert "ACTIVE" in verity_info, f"unexpected verity info: {verity_info}" + + backing_device = machine.succeed("df --output=source /nix/store | tail -n1").strip() + assert "/dev/mapper/usr" == backing_device, f"unexpected backing device: {backing_device}" + + tmp_disk_machine = create_disk_image( "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img", - "create", - "-f", - "qcow2", - "-b", "${nodes.machine.system.build.image}/${nodes.machine.image.filePath}", - "-F", - "raw", - tmp_disk_image.name, - ]) - - os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name - + ) + os.environ['NIX_DISK_IMAGE'] = tmp_disk_machine.name machine.wait_for_unit("default.target") + run_verity_tests(machine) + with subtest("Image version is set"): + machine.succeed("grep IMAGE_VERSION=1 /etc/os-release") - with subtest("Running with volatile root"): - machine.succeed("findmnt --kernel --type tmpfs /") - - with subtest("/nix/store is backed by dm-verity protected fs"): - verity_info = machine.succeed("dmsetup info --target verity usr") - assert "ACTIVE" in verity_info,f"unexpected verity info: {verity_info}" - - backing_device = machine.succeed("df --output=source /nix/store | tail -n1").strip() - assert "/dev/mapper/usr" == backing_device,"unexpected backing device: {backing_device}" + tmp_disk_without_version = create_disk_image( + "${nodes."without-version".virtualisation.qemu.package}/bin/qemu-img", + "${nodes."without-version".system.build.image}/${nodes."without-version".image.filePath}", + ) + os.environ['NIX_DISK_IMAGE'] = tmp_disk_without_version.name + without_version.wait_for_unit("default.target") + run_verity_tests(without_version) + with subtest("Image version is not set"): + without_version.succeed('grep IMAGE_VERSION="" /etc/os-release') ''; } From 5b822cf49fad270e04e203a01f65b15aab5057b5 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Wed, 7 Jan 2026 13:15:23 +0100 Subject: [PATCH 4/4] nixos/image/repart: move Minimize=best to module I don't think there's really a reason not to do this by default? --- nixos/modules/image/repart-verity-store.nix | 2 ++ nixos/tests/appliance-repart-image-verity-store.nix | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/nixos/modules/image/repart-verity-store.nix b/nixos/modules/image/repart-verity-store.nix index b396c445c617..3cc52ff8f2f1 100644 --- a/nixos/modules/image/repart-verity-store.nix +++ b/nixos/modules/image/repart-verity-store.nix @@ -96,6 +96,7 @@ in Verity = "hash"; VerityMatchKey = lib.mkDefault verityMatchKey; Label = lib.mkDefault "store-verity"; + Minimize = lib.mkDefault "best"; }; # dm-verity data partition that contains the nix store ${cfg.partitionIds.store} = { @@ -106,6 +107,7 @@ in Format = lib.mkDefault "erofs"; VerityMatchKey = lib.mkDefault verityMatchKey; Label = lib.mkDefault "store"; + Minimize = lib.mkDefault "best"; }; }; diff --git a/nixos/tests/appliance-repart-image-verity-store.nix b/nixos/tests/appliance-repart-image-verity-store.nix index 2199ab368f1f..39ebefa60eba 100644 --- a/nixos/tests/appliance-repart-image-verity-store.nix +++ b/nixos/tests/appliance-repart-image-verity-store.nix @@ -50,12 +50,6 @@ SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M"; }; }; - ${partitionIds.store-verity}.repartConfig = { - Minimize = "best"; - }; - ${partitionIds.store}.repartConfig = { - Minimize = "best"; - }; }; };