From 4959eee3f3b025333957e1162bced7ddd2746347 Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Sun, 3 May 2026 20:28:07 -0700 Subject: [PATCH 1/2] nixos/systemd/user: drop `with lib;` Drop the `with utils;`, `with systemdUtils.unitOptions;` and `with lib;` blocks and qualify all references with `lib.`, `utils.systemdUtils.lib.` and `utils.systemdUtils.types.` instead. No behavioural change. --- nixos/modules/system/boot/systemd/user.nix | 91 +++++++++++----------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix index 5161a5a2c2ff..baddfb1219ee 100644 --- a/nixos/modules/system/boot/systemd/user.nix +++ b/nixos/modules/system/boot/systemd/user.nix @@ -5,16 +5,13 @@ utils, ... }: -with utils; -with systemdUtils.unitOptions; -with lib; let cfg = config.systemd.user; systemd = config.systemd.package; - inherit (systemdUtils.lib) + inherit (utils.systemdUtils.lib) generateUnits targetToUnit serviceToUnit @@ -53,7 +50,7 @@ let user ? null, }: let - suffix = optionalString (user != null) "-${user}"; + suffix = lib.optionalString (user != null) "-${user}"; in pkgs.writeTextFile { name = "nixos-user-tmpfiles.d${suffix}"; @@ -61,15 +58,15 @@ let text = '' # This file is created automatically and should not be modified. # Please change the options ‘systemd.user.tmpfiles’ instead. - ${concatStringsSep "\n" rules} + ${lib.concatStringsSep "\n" rules} ''; }; in { options = { - systemd.user.extraConfig = mkOption { + systemd.user.extraConfig = lib.mkOption { default = ""; - type = types.lines; + type = lib.types.lines; example = "DefaultTimeoutStartSec=60"; description = '' Extra config options for systemd user instances. See {manpage}`systemd-user.conf(5)` for @@ -77,58 +74,58 @@ in ''; }; - systemd.user.units = mkOption { + systemd.user.units = lib.mkOption { description = "Definition of systemd per-user units."; default = { }; - type = systemdUtils.types.units; + type = utils.systemdUtils.types.units; }; - systemd.user.paths = mkOption { + systemd.user.paths = lib.mkOption { default = { }; - type = systemdUtils.types.paths; + type = utils.systemdUtils.types.paths; description = "Definition of systemd per-user path units."; }; - systemd.user.services = mkOption { + systemd.user.services = lib.mkOption { default = { }; - type = systemdUtils.types.services; + type = utils.systemdUtils.types.services; description = "Definition of systemd per-user service units."; }; - systemd.user.slices = mkOption { + systemd.user.slices = lib.mkOption { default = { }; - type = systemdUtils.types.slices; + type = utils.systemdUtils.types.slices; description = "Definition of systemd per-user slice units."; }; - systemd.user.sockets = mkOption { + systemd.user.sockets = lib.mkOption { default = { }; - type = systemdUtils.types.sockets; + type = utils.systemdUtils.types.sockets; description = "Definition of systemd per-user socket units."; }; - systemd.user.targets = mkOption { + systemd.user.targets = lib.mkOption { default = { }; - type = systemdUtils.types.targets; + type = utils.systemdUtils.types.targets; description = "Definition of systemd per-user target units."; }; - systemd.user.timers = mkOption { + systemd.user.timers = lib.mkOption { default = { }; - type = systemdUtils.types.timers; + type = utils.systemdUtils.types.timers; description = "Definition of systemd per-user timer units."; }; systemd.user.tmpfiles = { enable = - (mkEnableOption "systemd user units systemd-tmpfiles-setup.service and systemd-tmpfiles-clean.timer") + (lib.mkEnableOption "systemd user units systemd-tmpfiles-setup.service and systemd-tmpfiles-clean.timer") // { default = true; example = false; }; - rules = mkOption { - type = types.listOf types.str; + rules = lib.mkOption { + type = lib.types.listOf lib.types.str; default = [ ]; example = [ "D %C - - - 7d" ]; description = '' @@ -139,17 +136,17 @@ in ''; }; - users = mkOption { + users = lib.mkOption { description = '' Per-user rules for creation, deletion and cleaning of volatile and temporary files automatically. ''; default = { }; - type = types.attrsOf ( - types.submodule { + type = lib.types.attrsOf ( + lib.types.submodule { options = { - rules = mkOption { - type = types.listOf types.str; + rules = lib.mkOption { + type = lib.types.listOf lib.types.str; default = [ ]; example = [ "D %C - - - 7d" ]; description = '' @@ -165,8 +162,8 @@ in }; }; - systemd.user.generators = mkOption { - type = types.attrsOf types.path; + systemd.user.generators = lib.mkOption { + type = lib.types.attrsOf lib.types.path; default = { }; example = { systemd-gpt-auto-generator = "/dev/null"; @@ -179,9 +176,9 @@ in ''; }; - systemd.additionalUpstreamUserUnits = mkOption { + systemd.additionalUpstreamUserUnits = lib.mkOption { default = [ ]; - type = types.listOf types.str; + type = lib.types.listOf lib.types.str; example = [ ]; description = '' Additional units shipped with systemd that should be enabled for per-user systemd instances. @@ -210,22 +207,22 @@ in }; systemd.user.units = - mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit v)) cfg.paths - // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit v)) cfg.services - // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices - // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets - // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit v)) cfg.targets - // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers; + lib.mapAttrs' (n: v: lib.nameValuePair "${n}.path" (pathToUnit v)) cfg.paths + // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.service" (serviceToUnit v)) cfg.services + // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.slice" (sliceToUnit v)) cfg.slices + // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.socket" (socketToUnit v)) cfg.sockets + // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.target" (targetToUnit v)) cfg.targets + // lib.mapAttrs' (n: v: lib.nameValuePair "${n}.timer" (timerToUnit v)) cfg.timers; systemd.user.timers = { # enable systemd user tmpfiles - systemd-tmpfiles-clean.wantedBy = optional cfg.tmpfiles.enable "timers.target"; + systemd-tmpfiles-clean.wantedBy = lib.optional cfg.tmpfiles.enable "timers.target"; } # Generate timer units for all services that have a ‘startAt’ value. - // (mapAttrs (name: service: { + // (lib.mapAttrs (name: service: { wantedBy = [ "timers.target" ]; timerConfig.OnCalendar = service.startAt; - }) (filterAttrs (name: service: service.startAt != [ ]) cfg.services)); + }) (lib.filterAttrs (name: service: service.startAt != [ ]) cfg.services)); # Provide the systemd-user PAM service, required to run systemd # user instances. @@ -244,18 +241,18 @@ in systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions. # enable systemd user tmpfiles - systemd.user.services.systemd-tmpfiles-setup.wantedBy = optional cfg.tmpfiles.enable "basic.target"; + systemd.user.services.systemd-tmpfiles-setup.wantedBy = lib.optional cfg.tmpfiles.enable "basic.target"; # /run/current-system/sw/etc/xdg is in systemd's $XDG_CONFIG_DIRS so we can # write the tmpfiles.d rules for everyone there - environment.systemPackages = optional (cfg.tmpfiles.rules != [ ]) (writeTmpfiles { + environment.systemPackages = lib.optional (cfg.tmpfiles.rules != [ ]) (writeTmpfiles { inherit (cfg.tmpfiles) rules; }); # /etc/profiles/per-user/$USER/etc/xdg is in systemd's $XDG_CONFIG_DIRS so # we can write a single user's tmpfiles.d rules there - users.users = mapAttrs (user: cfg': { - packages = optional (cfg'.rules != [ ]) (writeTmpfiles { + users.users = lib.mapAttrs (user: cfg': { + packages = lib.optional (cfg'.rules != [ ]) (writeTmpfiles { inherit (cfg') rules; inherit user; }); From 0522a75d354be5d3de10291751d7d58fb4ab84a3 Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Sun, 3 May 2026 20:31:01 -0700 Subject: [PATCH 2/2] nixos/systemd/user: migrate to RFC 42-style settings Replace `systemd.user.extraConfig` with a freeform `systemd.user.settings.Manager` submodule, rendered via `utils.systemdUtils.lib.settingsToSections`. `extraConfig` is removed via `mkRemovedOptionModule`. Mirrors the existing `systemd.settings.Manager` migration of the system-side manager. Updates the two in-tree consumers (`nixos/modules/testing/test-instrumentation.nix` and `nixos/tests/systemd.nix`) to the new option. Adds `nixos/tests/systemd-user-settings` to assert the rendered `user.conf` contents. --- .../manual/release-notes/rl-2611.section.md | 2 ++ nixos/modules/system/boot/systemd/user.nix | 32 ++++++++++++------- .../modules/testing/test-instrumentation.nix | 8 ++--- nixos/tests/all-tests.nix | 1 + nixos/tests/systemd-user-settings.nix | 24 ++++++++++++++ nixos/tests/systemd.nix | 2 +- 6 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 nixos/tests/systemd-user-settings.nix diff --git a/nixos/doc/manual/release-notes/rl-2611.section.md b/nixos/doc/manual/release-notes/rl-2611.section.md index 3d53f1f20930..354c12f4d63e 100644 --- a/nixos/doc/manual/release-notes/rl-2611.section.md +++ b/nixos/doc/manual/release-notes/rl-2611.section.md @@ -23,6 +23,8 @@ no longer sets `programs.pqos-wrapper.enable = true` as the corresponding module has been deleted. +- `systemd.user.extraConfig` has been removed in favor of the structured [](#opt-systemd.user.settings.Manager) option. Use `systemd.user.settings.Manager` to set any `systemd-user.conf(5)` option directly. For example, replace `systemd.user.extraConfig = "DefaultTimeoutStartSec=60";` with `systemd.user.settings.Manager.DefaultTimeoutStartSec = 60;`. + ## Other Notable Changes {#sec-release-26.11-notable-changes} diff --git a/nixos/modules/system/boot/systemd/user.nix b/nixos/modules/system/boot/systemd/user.nix index baddfb1219ee..6a5b2657446b 100644 --- a/nixos/modules/system/boot/systemd/user.nix +++ b/nixos/modules/system/boot/systemd/user.nix @@ -63,14 +63,26 @@ let }; in { + imports = [ + (lib.mkRemovedOptionModule [ + "systemd" + "user" + "extraConfig" + ] "Use systemd.user.settings.Manager instead.") + ]; + options = { - systemd.user.extraConfig = lib.mkOption { - default = ""; - type = lib.types.lines; - example = "DefaultTimeoutStartSec=60"; + systemd.user.settings.Manager = lib.mkOption { + default = { }; + type = lib.types.submodule { + freeformType = lib.types.attrsOf utils.systemdUtils.unitOptions.unitOption; + }; + example = { + DefaultTimeoutStartSec = 60; + }; description = '' - Extra config options for systemd user instances. See {manpage}`systemd-user.conf(5)` for - available options. + Settings for systemd user instances. See {manpage}`systemd-user.conf(5)` + for available options. ''; }; @@ -200,10 +212,7 @@ in upstreamWants = [ ]; }; - "systemd/user.conf".text = '' - [Manager] - ${cfg.extraConfig} - ''; + "systemd/user.conf".text = utils.systemdUtils.lib.settingsToSections cfg.settings; }; systemd.user.units = @@ -241,7 +250,8 @@ in systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions. # enable systemd user tmpfiles - systemd.user.services.systemd-tmpfiles-setup.wantedBy = lib.optional cfg.tmpfiles.enable "basic.target"; + systemd.user.services.systemd-tmpfiles-setup.wantedBy = + lib.optional cfg.tmpfiles.enable "basic.target"; # /run/current-system/sw/etc/xdg is in systemd's $XDG_CONFIG_DIRS so we can # write the tmpfiles.d rules for everyone there diff --git a/nixos/modules/testing/test-instrumentation.nix b/nixos/modules/testing/test-instrumentation.nix index d697a5197272..4d20b20090b4 100644 --- a/nixos/modules/testing/test-instrumentation.nix +++ b/nixos/modules/testing/test-instrumentation.nix @@ -238,11 +238,11 @@ in ''; systemd.settings.Manager = managerSettings; - systemd.user.extraConfig = '' + systemd.user.settings.Manager = { # Allow very slow start - DefaultTimeoutStartSec=300 - DefaultDeviceTimeoutSec=300 - ''; + DefaultTimeoutStartSec = 300; + DefaultDeviceTimeoutSec = 300; + }; boot.consoleLogLevel = 7; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index e653dc42b9c2..cab52851cf6d 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1656,6 +1656,7 @@ in systemd-timesyncd-nscd-dnssec = runTest ./systemd-timesyncd-nscd-dnssec.nix; systemd-user-linger = runTest ./systemd-user-linger.nix; systemd-user-linger-purge = runTest ./systemd-user-linger-purge.nix; + systemd-user-settings = runTest ./systemd-user-settings.nix; systemd-user-tmpfiles-rules = runTest ./systemd-user-tmpfiles-rules.nix; systemd-userdbd = runTest ./systemd-userdbd.nix; systemtap = handleTest ./systemtap.nix { }; diff --git a/nixos/tests/systemd-user-settings.nix b/nixos/tests/systemd-user-settings.nix new file mode 100644 index 000000000000..8e70c07a8656 --- /dev/null +++ b/nixos/tests/systemd-user-settings.nix @@ -0,0 +1,24 @@ +{ + name = "systemd-user-settings"; + meta = { + maintainers = [ ]; + }; + + nodes.machine = + { lib, ... }: + { + systemd.user.settings.Manager = { + DefaultTimeoutStartSec = lib.mkForce "60"; + DefaultEnvironment = "FOO=bar"; + }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + + with subtest("settings.Manager renders user.conf"): + machine.succeed("grep -F '[Manager]' /etc/systemd/user.conf") + machine.succeed("grep -F 'DefaultTimeoutStartSec=60' /etc/systemd/user.conf") + machine.succeed("grep -F 'DefaultEnvironment=FOO=bar' /etc/systemd/user.conf") + ''; +} diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix index 98cc88a0326f..2dcce9aec6b0 100644 --- a/nixos/tests/systemd.nix +++ b/nixos/tests/systemd.nix @@ -34,7 +34,7 @@ RebootWatchdogSec = "10min"; KExecWatchdogSec = "5min"; }; - systemd.user.extraConfig = "DefaultEnvironment=\"XXX_USER=bar\""; + systemd.user.settings.Manager.DefaultEnvironment = "\"XXX_USER=bar\""; services.journald.extraConfig = "Storage=volatile"; test-support.displayManager.auto.user = "alice";