nixos/systemd/user: migrate to RFC 42-style settings (#516329)

This commit is contained in:
nikstur
2026-06-04 14:42:22 +00:00
committed by GitHub
6 changed files with 94 additions and 60 deletions
@@ -24,6 +24,8 @@
- Python 2 has been removed from the top-level package set, as it is long past end-of-life. The `python2`, `python27`, `python2Full`, `python27Full`, `python2Packages`, and `python27Packages` attributes, along with the legacy `python`, `pythonFull`, and `pythonPackages` aliases, now throw an error directing you to `python3`. The `isPy2` and `isPy27` package flags have been removed accordingly. The only remaining Python 2 interpreter is vendored inside the `resholve` package for its `oil` dependency and is not exposed for general use.
- `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}
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+62 -55
View File
@@ -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,74 +58,86 @@ 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
{
imports = [
(lib.mkRemovedOptionModule [
"systemd"
"user"
"extraConfig"
] "Use systemd.user.settings.Manager instead.")
];
options = {
systemd.user.extraConfig = mkOption {
default = "";
type = 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.
'';
};
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 +148,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 +174,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 +188,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.
@@ -203,29 +212,26 @@ in
upstreamWants = [ ];
};
"systemd/user.conf".text = ''
[Manager]
${cfg.extraConfig}
'';
"systemd/user.conf".text = utils.systemdUtils.lib.settingsToSections cfg.settings;
};
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 +250,19 @@ 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;
});
@@ -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;
+1
View File
@@ -1669,6 +1669,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 { };
+24
View File
@@ -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")
'';
}
+1 -1
View File
@@ -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";