I switched nearly all of my devices to NixOS and it is getting a bit tedious to update all of the systems. I came across colmena, which enabled me to update multiple devices via one command. I wanted to extend this concept by executing a colmena command using GitHub actions and connecting to the devices via Tailscale, but there is one problem: if Tailscale has an update, it restarts the service and cuts the connection!
I came across this thread, which says to use systemd.services.<name>.restartIfChanged
and disable the restart completely. This would work, but I would like to have the service up-to-date too, therefore a restart is needed. Is there any way to postpone the restart for a certain amount of time?
I was also thinking of create a new process to trigger the restart at a fixed time daily, but maybe there is a more elegant way 
1 Like
I tried feeding deepseek with the same question and it came up with a pretty convincing piece of config:
{ config, pkgs, ... }:
{
# Disable automatic restart of Tailscale on package update
systemd.services.tailscaled.restartIfChanged = false;
# Activation script to schedule delayed restart if Tailscale is updated
system.activationScripts.delayedTailscaleRestart = let
newTailscale = config.services.tailscale.package;
in ''
current_tailscale="$(readlink -f /run/current-system/sw/bin/tailscale 2>/dev/null || echo "")"
new_tailscale="$(readlink -f ${newTailscale}/bin/tailscale)"
if [ "$current_tailscale" != "$new_tailscale" ]; then
echo "Tailscale updated. Scheduling restart in 10 minutes..."
systemd-run --on-active=10m --unit=delayed-tailscale-restart systemctl restart tailscaled
fi
'';
}
I just applied it and will be testing it, when a new tailscale update drops 
Activation scripts are definitely not the way to do this. Activation is a critical bootup component and should only be used as a last resort. When something goes wrong with activation, it can be catastrophic to the system. A longterm goal among many NixOS maintainers is to one day deprecate custom activation scripts.
Furthermore, at bootup, /run/current-system
won’t be populated yet, and systemd-run
won’t be operable yet, so this is going to result in some funky error logs during bootup, but probably nothing catastrophic would happen.
You could do the same with a systemd timer unit that’s wanted by the sysinit-reactivation.target
unit. That target gets started on switch, so a timer unit with OnActiveSec=10m
would be started on switch, and 10 minutes later it would fire.
systemd.timers.restart-tailscale = {
wantedBy = [ "sysinit-reactivation.target" ];
timerConfig.OnActiveSec = "10m";
# Put timer in stopped state after it elapses,
# so it starts again next time.
timerConfig.RemainAfterElapse = false;
};
systemd.services.restart-tailscale = {
# no wantedBy
# no RemainAfterExit either
serviceConfig.Type = "oneshot";
script = ''
# determine if tailscale needs to restart and do it
...
'';
};
1 Like