5.6 KiB
Unit handling
To figure out what units need to be started/stopped/restarted/reloaded, the
script first checks the current state of the system, similar to what systemctl list-units shows. For each of the units, the script goes through the following
checks:
-
Is the unit file still in the new system? If not, stop the service unless it sets
X-StopOnRemovalin the[Unit]section tofalse. -
Is it a
.targetunit? If so, start it unless it setsRefuseManualStartin the[Unit]section totrueorX-OnlyManualStartin the[Unit]section totrue. Also stop the unit again unless it setsX-StopOnReconfigurationtofalse. -
Are the contents of the unit files different? They are compared by parsing them and comparing their contents. If they are different but only
X-Reload-Triggersin the[Unit]section is changed, reload the unit. The NixOS module system allows setting these triggers with the option systemd.services.<name>.reloadTriggers. There are some additional keys in the[Unit]section that are ignored as well. If the unit files differ in any way, the following actions are performed:-
.pathand.sliceunits are ignored. There is no need to restart them since changes in their values are applied by systemd when systemd is reloaded. -
.mountunits are reloaded if only theirOptionschanged. If anything else changed (likeWhat), they are restarted unless they are the mount unit for/or/nixin which case they are reloaded to prevent the system from crashing. Note that this is the case for.mountunits and not for mounts from/etc/fstab. These are explained in . -
.socketunits are currently ignored. This is to be fixed at a later point. -
The rest of the units (mostly
.serviceunits) are then reloaded ifX-ReloadIfChangedin the[Service]section is set totrue(exposed via systemd.services.<name>.reloadIfChanged). A little exception is done for units that were deactivated in the meantime, for example because they require a unit that got stopped before. These are started instead of reloaded. -
If the reload flag is not set, some more flags decide if the unit is skipped. These flags are
X-RestartIfChangedin the[Service]section (exposed via systemd.services.<name>.restartIfChanged),RefuseManualStopin the[Unit]section, andX-OnlyManualStartin the[Unit]section. -
Further behavior depends on the unit having
X-StopIfChangedin the[Service]section set totrue(exposed via systemd.services.<name>.stopIfChanged). This is set totrueby default and must be explicitly turned off if not wanted. If the flag is enabled, the unit is stopped and then started. If not, the unit is restarted. The goal of the flag is to make sure that the new unit never runs in the old environment which is still in place before the activation script is run. This behavior is different when the service is socket-activated, as outlined in the following steps. -
The last thing that is taken into account is whether the unit is a service and socket-activated. A correspondence between a
.serviceand its.socketunit is detected automatically, but services can opt out of that detection by settingX-NotSocketActivatedtoyesin their[Service]section. Otherwise, ifX-StopIfChangedis not set, the service is restarted with the others. If it is set, both the service and the socket are stopped and the socket is started, leaving socket activation to start the service when it's needed.
-
Sysinit reactivation
sysinit.target
is a systemd target that encodes system initialization (i.e. early startup). A
few units that need to run very early in the bootup process are ordered to
finish before this target is reached. Probably the most notable one of these is
systemd-tmpfiles-setup.service. We will refer to these units as "sysinit
units".
"Normal" systemd units, by default, are ordered AFTER sysinit.target. In
other words, these "normal" units expect all services ordered before
sysinit.target to have finished without explicitly declaring this dependency
relationship for each dependency. See the systemd
bootup
for more details on the bootup process.
When restarting both a unit ordered before sysinit.target as well as one
after, this presents a problem because they would be started at the same time
as they do not explicitly declare their dependency relations.
To solve this, NixOS has an artificial sysinit-reactivation.target which
allows you to ensure that services ordered before sysinit.target are
restarted correctly. This applies both to the ordering between these sysinit
services as well as ensuring that sysinit units are restarted before "normal"
units.
To make an existing sysinit service restart correctly during system switch, you have to declare:
{
systemd.services.my-sysinit = {
requiredBy = [ "sysinit-reactivation.target" ];
before = [ "sysinit-reactivation.target" ];
restartTriggers = [ config.environment.etc."my-sysinit.d".source ];
};
}
You need to configure appropriate restartTriggers specific to your service.