Sorry for the delayed response, hope it helps. Documenting anyway as it might help others as well.
The idea is to have a systemd target (tailscale-online.target
) that we ensure is enabled if and only if tailscale is up and vice versa.
To do that write and enable tailscale-dispatcher.timer
:
[Unit]
Description=Checks for tailscale state and syncs with `tailscale-online.target`
BindsTo=tailscaled.service
After=tailscaled.service
[Timer]
OnBootSec=0
OnUnitInactiveSec=10
AccuracySec=1
[Install]
WantedBy=tailscaled.service
The timer above will start tailscale-dispatcher.service
(no need to start or enable):
[Unit]
Description=Checks for tailscale state and syncs with `tailscale-online.target`
Requisite=tailscaled.service
After=tailscaled.service
[Service]
Type=oneshot
ExecStart=tailscale-dispatcher
The service above will run the script tailscale-dispatcher
(give executable perms and put in $PATH
:
#! /usr/bin/env zsh
get-state() {
if systemctl is-active --quiet tailscaled.service && [[ $(tailscale status --peers=false --json=true | jq -r '.Self.Online') = "true" ]]; then
current_state="online"
else
current_state="offline"
fi
}
transition() {
if [[ "$current_state" != "$prev_state" ]]; then
if [[ "$current_state" = "online" ]]; then
systemctl start tailscale-online.target
else
systemctl stop tailscale-online.target
fi
echo "$current_state" > /tmp/tailscale-state
fi
}
check-state() {
get-state
if [[ -s /tmp/tailscale-state ]]; then
prev_state=$(</tmp/tailscale-state)
else
prev_state="offline"
# uncomment if state has to be written even if still offline on all invocations before first transition to online
# touch /tmp/tailscale-state
# echo "$current_state" > /tmp/tailscale-state
fi
transition
}
main() {
check-state
}
main "$@"
Finally write tailscale-online.target
(no need to start or enable):
[Unit]
Description=Synced with tailscale status
BindsTo=tailscaled.service
After=tailscaled.service
Now if you have a service that works only in a tailnet (say nfs-mounter.service
), append the following to its service file and enable it (no need to start):
[Install]
RequiredBy=tailscale-online.target
Now nfs-mounter.service
will start and stop along with tailscale-online.target, as expected. Ymmv, but I’ve been using this in my work laptop for the past few months and it has been going well except when resuming out of hibernation with no Wi-Fi and open files, but connecting fixes that as well.
PS: I know it looks over-engineered and is literally equivalent to a cron bash script, but I wanted it to be generic enough to extend to other tail’ed services and also reuse the Dispatcher idiom followed by other network managers (NetworkManager and systemd-networkd for ex.).
PPS: I will allow any strongly-worded rebuke if you find any bash stupidities in the script. Not exactly my strong-suit.