From 61d18372d6029e17fe5f2460041ebc145eddcb0e Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Wed, 22 Apr 2026 23:10:30 -0400 Subject: [PATCH 1/2] nixos/systemd/initrd: Remove with lib --- nixos/modules/system/boot/systemd/initrd.nix | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 9727fe33d23c..a6c7d6f89f85 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -7,9 +7,29 @@ ... }: -with lib; - let + inherit (lib) + filter + elem + filterAttrs + concatLists + mapAttrsToList + getBin + concatStringsSep + mkEnableOption + mkOption + types + literalExpression + mkIf + any + isBool + isString + optionalAttrs + mapAttrs' + nameValuePair + listToAttrs + ; + inherit (utils) systemdUtils escapeSystemdPath; inherit (systemdUtils.unitOptions) unitOption; inherit (systemdUtils.lib) From 983d84d401c1a6295a0eccacbfaa04e63b879f3b Mon Sep 17 00:00:00 2001 From: Will Fancher Date: Wed, 22 Apr 2026 23:28:16 -0400 Subject: [PATCH 2/2] nixos/systemd/initrd: Check MODULES accurately `config.system.build.kernel.config` is not actually accurate. When a kconfig is specified in the `config` argument to `kernel/build.nix` (a.k.a. `manualConfig` or `linuxManualConfig`), then `isSet` will return `true` and other queries like `isYes` will be accurate. But if a kconfig is not specified in that `config` argument, then `isSet` will return `false` and other queries will be inaccurate, e.g. `isYes` "MODULES"` can return `false` even though your `configfile` has it enabled. Note the difference between the `config` argument and the `configfile` argument. The `configfile` is how the kernel will be actually built, while `config` is merely passed through as a source of eval-time information. Importantly, neither is derived from the other *in any way*, unless `builtins.isPath configfile || allowImportFromDerivation` in which case the default value for `config` is derived from reading `configfile`. The more generic `kernel/generic.nix` (a.k.a. `buildLinux`) creates its `configfile` in a derivation, so it cannot be read at eval time by default. It calls into `kernel/build.nix`, and only passes a `config` with `CONFIG_MODULES`, `CONFIG_FW_LOADER`, and `CONFIG_RUST` set. So almost nothing in `kernel.config` is accurate in the typical case. `MODULES` happens to be one of the three that *is* accurate typically, but regardless we obviously can't rely on that since a user of `kernel/build.nix` is likely to mess it up. Even worse, `structuredExtraConfig` is not incorporated into `kernel.config` at all, which leads to the incredibly confusing scenario where a kconfig is specified in `structuredExtraConfig` but still is not represented accurately by these queries. All of this is why, in most cases, the implementation of `requiredKernelConfig` deliberately does absolutely nothing and creates an empty list of assertions, and it's all extremely confusing. With all that in mind: TODO: - The structured config used to generate the `configfile` should be reflected in the `config` argument to `kernel/build.nix`, and consequently `kernel.config`. - The three kconfigs represented by `config` in `kernel/generic.nix` now, `CONFIG_MODULES`, `CONFIG_FW_LOADER`, and `CONFIG_RUST`, should be set in the structured config. - Queries for kconfigs that we don't actually know the value of at eval time should fail to evaluate, rather than evaluating inaccurately. - Most of the ways we use these eval-time queries should instead be done at build time, so they can use the complete `configfile` rather than the incomplete eval-time `config` value. - The ones that we still want to happen at eval-time should be more prepared for the possibility that we can't know the value of arbitrary kconfigs at eval time. --- Anyway, all that is to say: I'd like for this all to be better, but am not willing to work on the kernel expressions myself at the moment, so I thought I'd write down the reasons why this change was necessary, and the extent of the problem. We had already successfully considered this issue in one place in `systemd/initrd.nix`, but it seems there are two more places where we should have taken the same care. --- nixos/modules/system/boot/systemd/initrd.nix | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index a6c7d6f89f85..f242cb8c6a5b 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -47,6 +47,12 @@ let cfg = config.boot.initrd.systemd; + withKmod = + let + kconfig = config.system.build.kernel.config; + in + kconfig.isSet "MODULES" -> kconfig.isYes "MODULES"; + upstreamUnits = [ "basic.target" "breakpoint-pre-udev.service" @@ -525,7 +531,7 @@ in pkgs.coreutils cfg.package ] - ++ lib.optional (config.system.build.kernel.config.isYes "MODULES") cfg.package.kmod + ++ lib.optional withKmod cfg.package.kmod ++ lib.optionals cfg.shell.enable [ # bashInteractive is easier to use and also required by debug-shell.service pkgs.bashInteractive @@ -575,7 +581,7 @@ in // optionalAttrs (config.environment.etc ? "modprobe.d/nixos.conf") { "/etc/modprobe.d/nixos.conf".source = config.environment.etc."modprobe.d/nixos.conf".source; } - // optionalAttrs (with config.system.build.kernel.config; isSet "MODULES" -> isYes "MODULES") { + // optionalAttrs withKmod { "/lib".source = "${config.system.build.modulesClosure}/lib"; "/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules; @@ -660,7 +666,7 @@ in ) cfg.automounts ); - services."modprobe@" = lib.mkIf (config.system.build.kernel.config.isYes "MODULES") { + services."modprobe@" = lib.mkIf withKmod { serviceConfig.ExecSearchPath = lib.makeBinPath [ cfg.package.kmod ]; };