Skip to content

Instantly share code, notes, and snippets.

@willjasen
Last active May 24, 2024 21:30
Show Gist options
  • Save willjasen/41c14dbc402e4168ea13a93d8a847a2f to your computer and use it in GitHub Desktop.
Save willjasen/41c14dbc402e4168ea13a93d8a847a2f to your computer and use it in GitHub Desktop.
Debian systemd dependencies on Tailscale

Prologue

Some services in Debian/Ubuntu need to start after the Tailscale service is not only started/active, but has fully come up (in so much that it passes network traffic, which can take 5-10 seconds after the service starts). This is crucial when binding services solely to the Tailscale interface such that they require it to be fully operational before binding can successfully complete. Some services like the Zabbix agent may initially fail but will retry and start successfully once Tailscale is fully operational, but other services like netatalk (used for Apple file sharing) will generally fail to start or bind and will not reattempt, forcing a manual intervention of restarting that service.

To account for this, a "ExecStartPost" within the Tailscale systemd config monitors when the host can successfully ping 100.100.100.100 (the "localhost" IP within Tailscale) such that other services that depend on Tailscale won't attempt to start until the ping is successful. From there, the dependent services (zabbix-agent2, netatalk, etc.) only need the "After" and "Requires" sections to have tailscaled.service listed.

Implementation

Create the directory for each of the systemd service overrides : (supports multiple services)

DEPENDENT_SERVICES=(netatalk zabbix-agent2);

mkdir -p /etc/systemd/system/tailscaled.service.d;
for SERVICE in ${DEPENDENT_SERVICES[@]}; do
  mkdir -p /etc/systemd/system/$SERVICE.service.d;
done;

Create the tailscaled override :

#DROP_IN=/etc/systemd/system/tailscaled.service.d/wait-for-tailnet-up.conf;   ## isn't working
DROP_IN=/etc/systemd/system/tailscaled.service.d/override.conf;
rm $DROP_IN 2> /dev/null;
echo "[Service]" | tee -a $DROP_IN >/dev/null;
echo "ExecStartPost=/bin/bash -c '(while ! ping -c1 100.100.100.100 >/dev/null; do sleep 1; done);'" | tee -a $DROP_IN >/dev/null;

Create the dependent app overrides:


for SERVICE in ${DEPENDENT_SERVICES[@]}; do
  #DROP_IN=/etc/systemd/system/$SERVICE.service.d/wait-for-tailscaled.conf;   ## isn't working
  DROP_IN=/etc/systemd/system/$SERVICE.service.d/override.conf;
  rm $DROP_IN 2> /dev/null;
  echo "[Unit]" | tee -a $DROP_IN >/dev/null;
  echo "After=tailscaled.service" | tee -a $DROP_IN >/dev/null;
  echo "Requires=tailscaled.service" | tee -a $DROP_IN >/dev/null;
done;

To verify that the config files were created, run : (this creates a temporary override.conf file which is a copy of the original all commented out with the override config within)

systemctl edit tailscaled       ## --drop-in=wait-for-tailnet-up
systemctl edit netatalk         ## --drop-in=wait-for-tailscaled
systemctl edit zabbix-agent2    ## --drop-in=wait-for-tailscaled

To have these changes take effect, run the command :

systemctl daemon-reload

By performing these steps, it should ensure that the service(s) start after the tailscaled service starts and also waits for Tailscale to be ready to handle network traffic.

References

Credits

  • @shladek - idea to decouple the extra config from the installed one from package
@shladek
Copy link

shladek commented May 24, 2024

Modifying the original unit file is not recommended. You probably should create a new replacement unit file or specific drop-in snippets.

In your example, I'd probably just do
systemctl edit tailscaled --drop-in=customexec and have the following

[Service]
ExecStartPost=/bin/bash -c '(while ! ping -c1 100.100.100.100 >/dev/null; do echo "Waiting for the local Tailscale interface..."; sleep 1; done);'

Then if there are problems or you just want to revert back to tailscale's original unit file:
systemctl revert tailscaled

@willjasen
Copy link
Author

willjasen commented May 24, 2024

Thank you for this suggestion! I have updated the guide in such a way that works but relies on override.conf when using systemctl edit tailscaled with no options.

For some reason, when I run systemctl edit tailscaled --drop-in=wait-for-tailnet-up, I get systemctl: unrecognized option '--drop-in=wait-for-tailnet-up', so I worked around by implementing with override.conf by using systemctl edit tailscaled as is.

Despite the file being named override.conf, it acts more as an appending operation, the same as a drop-in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment