1 { config, lib, pkgs, utils, ... }:
4 with systemdUtils.unitOptions;
11 inherit (systemdUtils.lib)
32 "network-online.target"
34 "nss-user-lookup.target"
36 "first-boot-complete.target"
37 ] ++ optionals cfg.package.withCryptsetup [
39 "cryptsetup-pre.target"
40 "remote-cryptsetup.target"
51 # systemd-debug-generator
55 "systemd-udevd-control.socket"
56 "systemd-udevd-kernel.socket"
57 "systemd-udevd.service"
58 "systemd-udev-settle.service"
59 ] ++ (optional (!config.boot.isContainer) "systemd-udev-trigger.service") ++ [
60 # hwdb.bin is managed by NixOS
61 # "systemd-hwdb-update.service"
67 "serial-getty@.service"
68 "console-getty.service"
69 "container-getty@.service"
70 "systemd-vconsole-setup.service"
72 # Hardware (started by udev when a relevant device is plugged in).
78 # Kernel module loading.
79 "systemd-modules-load.service"
80 "kmod-static-nodes.service"
84 "systemd-fsck@.service"
85 "systemd-fsck-root.service"
86 "systemd-growfs@.service"
87 "systemd-growfs-root.service"
88 "systemd-remount-fs.service"
89 "systemd-pstore.service"
93 "remote-fs-pre.target"
97 "sys-fs-fuse-connections.mount"
98 ] ++ (optional (!config.boot.isContainer) "sys-kernel-config.mount") ++ [
99 "sys-kernel-debug.mount"
101 # Maintaining state across reboots.
102 "systemd-random-seed.service"
103 ] ++ (optional cfg.package.withBootloader "systemd-boot-random-seed.service") ++ [
104 "systemd-backlight@.service"
105 "systemd-rfkill.service"
106 "systemd-rfkill.socket"
108 # Hibernate / suspend.
111 "suspend-then-hibernate.target"
113 "hybrid-sleep.target"
114 "systemd-hibernate.service"
115 "systemd-hibernate-clear.service"
116 "systemd-hybrid-sleep.service"
117 "systemd-suspend.service"
118 "systemd-suspend-then-hibernate.service"
122 "systemd-reboot.service"
124 "systemd-poweroff.service"
126 "systemd-halt.service"
131 "systemd-kexec.service"
132 ] ++ lib.optional cfg.package.withUtmp "systemd-update-utmp.service" ++ [
135 "systemd-ask-password-console.path"
136 "systemd-ask-password-console.service"
137 "systemd-ask-password-wall.path"
138 "systemd-ask-password-wall.service"
141 "systemd-bootctl@.service"
142 "systemd-bootctl.socket"
143 "systemd-creds@.service"
144 "systemd-creds.socket"
145 ] ++ lib.optional cfg.package.withTpm2Tss [
146 "systemd-pcrlock@.service"
147 "systemd-pcrlock.socket"
150 # Slices / containers.
152 ] ++ optionals cfg.package.withImportd [
153 "systemd-importd.service"
154 ] ++ optionals cfg.package.withMachined [
157 "systemd-machined.service"
159 "systemd-nspawn@.service"
162 "systemd-sysctl.service"
163 ] ++ optionals cfg.package.withTimedated [
164 "dbus-org.freedesktop.timedate1.service"
165 "systemd-timedated.service"
166 ] ++ optionals cfg.package.withLocaled [
167 "dbus-org.freedesktop.locale1.service"
168 "systemd-localed.service"
169 ] ++ optionals cfg.package.withHostnamed [
170 "dbus-org.freedesktop.hostname1.service"
171 "systemd-hostnamed.service"
172 "systemd-hostnamed.socket"
173 ] ++ optionals cfg.package.withPortabled [
174 "dbus-org.freedesktop.portable1.service"
175 "systemd-portabled.service"
177 "systemd-exit.service"
178 "systemd-update-done.service"
179 ] ++ cfg.additionalUpstreamSystemUnits;
181 upstreamSystemWants =
182 [ "sysinit.target.wants"
183 "sockets.target.wants"
184 "local-fs.target.wants"
185 "multi-user.target.wants"
186 "timers.target.wants"
189 proxy_env = config.networking.proxy.envVars;
198 package = mkPackageOption pkgs "systemd" {};
200 enableStrictShellChecks = mkEnableOption "running shellcheck on the generated scripts for systemd units.";
203 description = "Definition of systemd units; see {manpage}`systemd.unit(5)`.";
205 type = systemdUtils.types.units;
208 packages = mkOption {
210 type = types.listOf types.package;
211 example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
212 description = "Packages providing systemd units and hooks.";
217 type = systemdUtils.types.targets;
218 description = "Definition of systemd target units; see {manpage}`systemd.target(5)`";
221 services = mkOption {
223 type = systemdUtils.types.services;
224 description = "Definition of systemd service units; see {manpage}`systemd.service(5)`.";
229 type = systemdUtils.types.sockets;
230 description = "Definition of systemd socket units; see {manpage}`systemd.socket(5)`.";
235 type = systemdUtils.types.timers;
236 description = "Definition of systemd timer units; see {manpage}`systemd.timer(5)`.";
241 type = systemdUtils.types.paths;
242 description = "Definition of systemd path units; see {manpage}`systemd.path(5)`.";
247 type = systemdUtils.types.mounts;
249 Definition of systemd mount units; see {manpage}`systemd.mount(5)`.
251 This is a list instead of an attrSet, because systemd mandates
252 the names to be derived from the `where` attribute.
256 automounts = mkOption {
258 type = systemdUtils.types.automounts;
260 Definition of systemd automount units; see {manpage}`systemd.automount(5)`.
262 This is a list instead of an attrSet, because systemd mandates
263 the names to be derived from the `where` attribute.
269 type = systemdUtils.types.slices;
270 description = "Definition of slice configurations; see {manpage}`systemd.slice(5)`.";
273 generators = mkOption {
274 type = types.attrsOf types.path;
276 example = { systemd-gpt-auto-generator = "/dev/null"; };
278 Definition of systemd generators; see {manpage}`systemd.generator(5)`.
280 For each `NAME = VALUE` pair of the attrSet, a link is generated from
281 `/etc/systemd/system-generators/NAME` to `VALUE`.
285 shutdown = mkOption {
286 type = types.attrsOf types.path;
289 Definition of systemd shutdown executables.
290 For each `NAME = VALUE` pair of the attrSet, a link is generated from
291 `/etc/systemd/system-shutdown/NAME` to `VALUE`.
295 defaultUnit = mkOption {
296 default = "multi-user.target";
299 Default unit started when the system boots; see {manpage}`systemd.special(7)`.
303 ctrlAltDelUnit = mkOption {
304 default = "reboot.target";
306 example = "poweroff.target";
308 Target that should be started when Ctrl-Alt-Delete is pressed;
309 see {manpage}`systemd.special(7)`.
313 globalEnvironment = mkOption {
314 type = with types; attrsOf (nullOr (oneOf [ str path package ]));
316 example = { TZ = "CET"; };
318 Environment variables passed to *all* systemd units.
322 managerEnvironment = mkOption {
323 type = with types; attrsOf (nullOr (oneOf [ str path package ]));
325 example = { SYSTEMD_LOG_LEVEL = "debug"; };
327 Environment variables of PID 1. These variables are
328 *not* passed to started units.
332 enableCgroupAccounting = mkOption {
336 Whether to enable cgroup accounting; see {manpage}`cgroups(7)`.
340 extraConfig = mkOption {
343 example = "DefaultLimitCORE=infinity";
345 Extra config options for systemd. See {manpage}`systemd-system.conf(5)` man page
346 for available options.
350 sleep.extraConfig = mkOption {
353 example = "HibernateDelaySec=1h";
355 Extra config options for systemd sleep state logic.
356 See {manpage}`sleep.conf.d(5)` man page for available options.
360 additionalUpstreamSystemUnits = mkOption {
362 type = types.listOf types.str;
363 example = [ "debug-shell.service" "systemd-quotacheck.service" ];
365 Additional units shipped with systemd that shall be enabled.
369 suppressedSystemUnits = mkOption {
371 type = types.listOf types.str;
372 example = [ "systemd-backlight@.service" ];
374 A list of units to skip when generating system systemd configuration directory. This has
375 priority over upstream units, {option}`systemd.units`, and
376 {option}`systemd.additionalUpstreamSystemUnits`. The main purpose of this is to
377 prevent a upstream systemd unit from being added to the initrd with any modifications made to it
378 by other NixOS modules.
382 watchdog.device = mkOption {
383 type = types.nullOr types.path;
385 example = "/dev/watchdog";
387 The path to a hardware watchdog device which will be managed by systemd.
388 If not specified, systemd will default to `/dev/watchdog`.
392 watchdog.runtimeTime = mkOption {
393 type = types.nullOr types.str;
397 The amount of time which can elapse before a watchdog hardware device
398 will automatically reboot the system.
400 Valid time units include "ms", "s", "min", "h", "d", and "w";
401 see {manpage}`systemd.time(7)`.
405 watchdog.rebootTime = mkOption {
406 type = types.nullOr types.str;
410 The amount of time which can elapse after a reboot has been triggered
411 before a watchdog hardware device will automatically reboot the system.
412 If left `null`, systemd will use its default of 10 minutes;
413 see {manpage}`systemd-system.conf(5)`.
415 Valid time units include "ms", "s", "min", "h", "d", and "w";
416 see also {manpage}`systemd.time(7)`.
420 watchdog.kexecTime = mkOption {
421 type = types.nullOr types.str;
425 The amount of time which can elapse when `kexec` is being executed before
426 a watchdog hardware device will automatically reboot the system. This
427 option should only be enabled if `reloadTime` is also enabled;
428 see {manpage}`kexec(8)`.
430 Valid time units include "ms", "s", "min", "h", "d", and "w";
431 see also {manpage}`systemd.time(7)`.
437 ###### implementation
442 mkOneNetOnlineWarn = typeStr: name: def: lib.optional
443 (lib.elem "network-online.target" def.after && !(lib.elem "network-online.target" (def.wants ++ def.requires ++ def.bindsTo)))
444 "${name}.${typeStr} is ordered after 'network-online.target' but doesn't depend on it";
445 mkNetOnlineWarns = typeStr: defs: lib.concatLists (lib.mapAttrsToList (mkOneNetOnlineWarn typeStr) defs);
446 mkMountNetOnlineWarns = typeStr: defs: lib.concatLists (map (m: mkOneNetOnlineWarn typeStr m.what m) defs);
451 type = service.serviceConfig.Type or "";
452 restart = service.serviceConfig.Restart or "no";
453 hasDeprecated = builtins.hasAttr "StartLimitInterval" service.serviceConfig;
456 (optional (type == "oneshot" && (restart == "always" || restart == "on-success"))
457 "Service '${name}.service' with 'Type=oneshot' cannot have 'Restart=always' or 'Restart=on-success'"
459 (optional hasDeprecated
460 "Service '${name}.service' uses the attribute 'StartLimitInterval' in the Service section, which is deprecated. See https://github.com/NixOS/nixpkgs/issues/45786."
462 (optional (service.reloadIfChanged && service.reloadTriggers != [])
463 "Service '${name}.service' has both 'reloadIfChanged' and 'reloadTriggers' set. This is probably not what you want, because 'reloadTriggers' behave the same whay as 'restartTriggers' if 'reloadIfChanged' is set."
469 ++ (mkNetOnlineWarns "target" cfg.targets)
470 ++ (mkNetOnlineWarns "service" cfg.services)
471 ++ (mkNetOnlineWarns "socket" cfg.sockets)
472 ++ (mkNetOnlineWarns "timer" cfg.timers)
473 ++ (mkNetOnlineWarns "path" cfg.paths)
474 ++ (mkMountNetOnlineWarns "mount" cfg.mounts)
475 ++ (mkMountNetOnlineWarns "automount" cfg.automounts)
476 ++ (mkNetOnlineWarns "slice" cfg.slices);
478 assertions = concatLists (
485 (optional ((builtins.elem "network-interfaces.target" service.after) || (builtins.elem "network-interfaces.target" service.wants))
486 "Service '${name}.service' is using the deprecated target network-interfaces.target, which no longer exists. Using network.target is recommended instead."
493 system.build.units = cfg.units;
495 system.nssModules = [ cfg.package.out ];
496 system.nssDatabases = {
498 (mkOrder 400 ["mymachines"]) # 400 to ensure it comes before resolve (which is 501)
499 (mkOrder 999 ["myhostname"]) # after files (which is 998), but before regular nss modules
502 (mkAfter [ "systemd" ])
505 (mkAfter [ "[success=merge] systemd" ]) # need merge so that NSS won't stop at file-based groups
509 environment.systemPackages = [ cfg.package ];
511 environment.etc = let
512 # generate contents for /etc/systemd/${dir} from attrset of links and packages
513 hooks = dir: links: pkgs.runCommand "${dir}" {
514 preferLocalBuild = true;
515 packages = cfg.packages;
519 for package in $packages
521 for hook in $package/lib/systemd/${dir}/*
526 ${concatStrings (mapAttrsToList (exec: target: "ln -s ${target} $out/${exec};\n") links)}
529 enabledUpstreamSystemUnits = filter (n: ! elem n cfg.suppressedSystemUnits) upstreamSystemUnits;
530 enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedSystemUnits) cfg.units;
533 "systemd/system".source = generateUnits {
535 units = enabledUnits;
536 upstreamUnits = enabledUpstreamSystemUnits;
537 upstreamWants = upstreamSystemWants;
540 "systemd/system.conf".text = ''
542 ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)}
543 ${optionalString cfg.enableCgroupAccounting ''
544 DefaultCPUAccounting=yes
545 DefaultIOAccounting=yes
546 DefaultBlockIOAccounting=yes
547 DefaultIPAccounting=yes
549 DefaultLimitCORE=infinity
550 ${optionalString (cfg.watchdog.device != null) ''
551 WatchdogDevice=${cfg.watchdog.device}
553 ${optionalString (cfg.watchdog.runtimeTime != null) ''
554 RuntimeWatchdogSec=${cfg.watchdog.runtimeTime}
556 ${optionalString (cfg.watchdog.rebootTime != null) ''
557 RebootWatchdogSec=${cfg.watchdog.rebootTime}
559 ${optionalString (cfg.watchdog.kexecTime != null) ''
560 KExecWatchdogSec=${cfg.watchdog.kexecTime}
566 "systemd/sleep.conf".text = ''
568 ${cfg.sleep.extraConfig}
571 "systemd/user-generators" = { source = hooks "user-generators" cfg.user.generators; };
572 "systemd/system-generators" = { source = hooks "system-generators" cfg.generators; };
573 "systemd/system-shutdown" = { source = hooks "system-shutdown" cfg.shutdown; };
575 # Ignore all other preset files so systemd doesn't try to enable/disable
576 # units during runtime.
577 "systemd/system-preset/00-nixos.preset".text = ''
580 "systemd/user-preset/00-nixos.preset".text = ''
585 services.dbus.enable = true;
587 users.users.systemd-network = {
588 uid = config.ids.uids.systemd-network;
589 group = "systemd-network";
591 users.groups.systemd-network.gid = config.ids.gids.systemd-network;
592 users.users.systemd-resolve = {
593 uid = config.ids.uids.systemd-resolve;
594 group = "systemd-resolve";
596 users.groups.systemd-resolve.gid = config.ids.gids.systemd-resolve;
598 # Target for ‘charon send-keys’ to hook into.
599 users.groups.keys.gid = config.ids.gids.keys;
601 systemd.targets.keys =
602 { description = "Security Keys";
603 unitConfig.X-StopOnReconfiguration = true;
606 # This target only exists so that services ordered before sysinit.target
607 # are restarted in the correct order, notably BEFORE the other services,
608 # when switching configurations.
609 systemd.targets.sysinit-reactivation = {
610 description = "Reactivate sysinit units";
615 withName = cfgToUnit: cfg: lib.nameValuePair cfg.name (cfgToUnit cfg);
617 mapAttrs' (_: withName pathToUnit) cfg.paths
618 // mapAttrs' (_: withName serviceToUnit) cfg.services
619 // mapAttrs' (_: withName sliceToUnit) cfg.slices
620 // mapAttrs' (_: withName socketToUnit) cfg.sockets
621 // mapAttrs' (_: withName targetToUnit) cfg.targets
622 // mapAttrs' (_: withName timerToUnit) cfg.timers
623 // listToAttrs (map (withName mountToUnit) cfg.mounts)
624 // listToAttrs (map (withName automountToUnit) cfg.automounts);
626 # Environment of PID 1
627 systemd.managerEnvironment = {
628 # Doesn't contain systemd itself - everything works so it seems to use the compiled-in value for its tools
629 # util-linux is needed for the main fsck utility wrapping the fs-specific ones
630 PATH = lib.makeBinPath (config.system.fsPackages ++ [cfg.package.util-linux]);
631 LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
632 TZDIR = "/etc/zoneinfo";
633 # If SYSTEMD_UNIT_PATH ends with an empty component (":"), the usual unit load path will be appended to the contents of the variable
634 SYSTEMD_UNIT_PATH = lib.mkIf (config.boot.extraSystemdUnitPaths != []) "${builtins.concatStringsSep ":" config.boot.extraSystemdUnitPaths}:";
638 system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
639 [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
640 "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"
641 "CRYPTO_SHA256" "DMIID" "AUTOFS_FS" "TMPFS_POSIX_ACL"
642 "TMPFS_XATTR" "SECCOMP"
645 # Generate timer units for all services that have a ‘startAt’ value.
647 mapAttrs (name: service:
648 { wantedBy = [ "timers.target" ];
649 timerConfig.OnCalendar = service.startAt;
651 (filterAttrs (name: service: service.enable && service.startAt != []) cfg.services);
653 # Some overrides to upstream units.
654 systemd.services."systemd-backlight@".restartIfChanged = false;
655 systemd.services."systemd-fsck@".restartIfChanged = false;
656 systemd.services."systemd-fsck@".path = [ pkgs.util-linux ] ++ config.system.fsPackages;
657 systemd.services."systemd-makefs@" = {
658 restartIfChanged = false;
659 path = [ pkgs.util-linux ] ++ config.system.fsPackages;
660 # Since there is no /etc/systemd/system/systemd-makefs@.service
661 # file, the units generated in /run/systemd/generator would
662 # override anything we put here. But by forcing the use of a
663 # drop-in in /etc, it does apply.
664 overrideStrategy = "asDropin";
666 systemd.services."systemd-mkswap@" = {
667 restartIfChanged = false;
668 path = [ pkgs.util-linux ];
669 overrideStrategy = "asDropin";
671 systemd.services.systemd-random-seed.restartIfChanged = false;
672 systemd.services.systemd-remount-fs.restartIfChanged = false;
673 systemd.services.systemd-update-utmp.restartIfChanged = false;
674 systemd.services.systemd-udev-settle.restartIfChanged = false; # Causes long delays in nixos-rebuild
675 systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
676 systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
677 systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
678 systemd.services.systemd-importd.environment = proxy_env;
679 systemd.services.systemd-pstore.wantedBy = [ "sysinit.target" ]; # see #81138
681 # NixOS has kernel modules in a different location, so override that here.
682 systemd.services.kmod-static-nodes.unitConfig.ConditionFileNotEmpty = [
683 "" # required to unset the previous value!
684 "/run/booted-system/kernel-modules/lib/modules/%v/modules.devname"
687 # Don't bother with certain units in containers.
688 systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
690 # Increase numeric PID range (set directly instead of copying a one-line file from systemd)
691 # https://github.com/systemd/systemd/pull/12226
692 boot.kernel.sysctl."kernel.pid_max" = mkIf pkgs.stdenv.hostPlatform.is64bit (lib.mkDefault 4194304);
694 services.logrotate.settings = {
695 "/var/log/btmp" = mapAttrs (_: mkDefault) {
696 frequency = "monthly";
698 create = "0660 root ${config.users.groups.utmp.name}";
701 "/var/log/wtmp" = mapAttrs (_: mkDefault) {
702 frequency = "monthly";
704 create = "0664 root ${config.users.groups.utmp.name}";
710 # FIXME: Remove these eventually.
712 [ (mkRenamedOptionModule [ "boot" "systemd" "sockets" ] [ "systemd" "sockets" ])
713 (mkRenamedOptionModule [ "boot" "systemd" "targets" ] [ "systemd" "targets" ])
714 (mkRenamedOptionModule [ "boot" "systemd" "services" ] [ "systemd" "services" ])
715 (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ])
716 (mkRemovedOptionModule [ "systemd" "generator-packages" ] "Use systemd.packages instead.")
717 (mkRemovedOptionModule ["systemd" "enableUnifiedCgroupHierarchy"] ''
718 In 256 support for cgroup v1 ('legacy' and 'hybrid' hierarchies) is now considered obsolete and systemd by default will refuse to boot under it.
719 To forcibly reenable cgroup v1 support, you can set boot.kernelParams = [ "systemd.unified_cgroup_hierachy=0" "SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1" ].
720 NixOS does not officially support this configuration and might cause your system to be unbootable in future versions. You are on your own.