diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index a90f58042d2d..9a9d35c08db9 100755
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -167,10 +167,6 @@ exec 1>&$logOutFd 2>&$logErrFd
exec {logOutFd}>&- {logErrFd}>&-
-# Start systemd.
+# Start systemd in a clean environment.
echo "starting systemd..."
-
-PATH=/run/current-system/systemd/lib/systemd:@fsPackagesPath@ \
- LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive @systemdUnitPathEnvVar@ \
- TZDIR=/etc/zoneinfo \
- exec @systemdExecutable@
+exec env - @systemdExecutable@ "$@"
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index f6b6a8e4b0b4..26fad8e7139f 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -19,11 +19,6 @@ let
pkgs.coreutils
pkgs.util-linux
] ++ lib.optional useHostResolvConf pkgs.openresolv);
- fsPackagesPath = lib.makeBinPath config.system.fsPackages;
- systemdUnitPathEnvVar = lib.optionalString (config.boot.extraSystemdUnitPaths != [])
- ("SYSTEMD_UNIT_PATH="
- + builtins.concatStringsSep ":" config.boot.extraSystemdUnitPaths
- + ":"); # If SYSTEMD_UNIT_PATH ends with an empty component (":"), the usual unit load path will be appended to the contents of the variable
postBootCommands = pkgs.writeText "local-cmds"
''
${config.boot.postBootCommands}
@@ -78,12 +73,10 @@ in
};
systemdExecutable = mkOption {
- default = "systemd";
+ default = "/run/current-system/systemd/lib/systemd/systemd";
type = types.str;
description = ''
- The program to execute to start systemd. Typically
- systemd, which will find systemd in the
- PATH.
+ The program to execute to start systemd.
'';
};
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index 057474c607ac..297a80d4681b 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -302,6 +302,16 @@ in
'';
};
+ systemd.managerEnvironment = mkOption {
+ type = with types; attrsOf (nullOr (oneOf [ str path package ]));
+ default = {};
+ example = { SYSTEMD_LOG_LEVEL = "debug"; };
+ description = ''
+ Environment variables of PID 1. These variables are
+ not passed to started units.
+ '';
+ };
+
systemd.enableCgroupAccounting = mkOption {
default = true;
type = types.bool;
@@ -470,11 +480,13 @@ in
enabledUpstreamSystemUnits = filter (n: ! elem n cfg.suppressedSystemUnits) upstreamSystemUnits;
enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedSystemUnits) cfg.units;
+
in ({
"systemd/system".source = generateUnits "system" enabledUnits enabledUpstreamSystemUnits upstreamSystemWants;
"systemd/system.conf".text = ''
[Manager]
+ ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)}
${optionalString config.systemd.enableCgroupAccounting ''
DefaultCPUAccounting=yes
DefaultIOAccounting=yes
@@ -542,6 +554,17 @@ in
(v: let n = escapeSystemdPath v.where;
in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
+ # Environment of PID 1
+ systemd.managerEnvironment = {
+ # Doesn't contain systemd itself - everything works so it seems to use the compiled-in value for its tools
+ PATH = lib.makeBinPath config.system.fsPackages;
+ LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
+ TZDIR = "/etc/zoneinfo";
+ # If SYSTEMD_UNIT_PATH ends with an empty component (":"), the usual unit load path will be appended to the contents of the variable
+ SYSTEMD_UNIT_PATH = lib.mkIf (config.boot.extraSystemdUnitPaths != []) "${builtins.concatStringsSep ":" config.boot.extraSystemdUnitPaths}:";
+ };
+
+
system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
[ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
"SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"
diff --git a/nixos/tests/systemd.nix b/nixos/tests/systemd.nix
index f86daa5eea97..94805ee5781a 100644
--- a/nixos/tests/systemd.nix
+++ b/nixos/tests/systemd.nix
@@ -192,5 +192,9 @@ import ./make-test-python.nix ({ pkgs, ... }: {
with subtest("systemd per-unit accounting works"):
assert "IP traffic received: 84B" in output_ping
assert "IP traffic sent: 84B" in output_ping
+
+ with subtest("systemd environment is properly set"):
+ machine.systemctl("daemon-reexec") # Rewrites /proc/1/environ
+ machine.succeed("grep -q TZDIR=/etc/zoneinfo /proc/1/environ")
'';
})