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.
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.
- @shladek - idea to decouple the extra config from the installed one from package
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 followingThen if there are problems or you just want to revert back to tailscale's original unit file:
systemctl revert tailscaled