runsv is very simple:
starts and monitors a service and optionally an appendant log service1
There is no integrated way of saying “I need X to start only if/after Y is running” other than doing it yourself. In my case I need Docker to come up only after Tailscale is fully running: I have some containers whose ports are exposed only through the Tailscale interface, and when I restart the system they sometimes have no ports exposed at all because Docker restarted them (I use for most of them restart: unless-stopped) too early.
The original /etc/sv/docker/run is as follows:
#!/bin/sh
exec 2>&1
[ -r conf ] && . ./conf
modprobe -q loop || exit 1
exec chpst -o 1048576 -p 1048576 dockerd $OPTS 2>&1
Very simple, as most run scripts are. I will admit that the evil gnome that lives in the back of my head suggested:
You see, in the logs
tailscaledalways starts at max 1 second afterdocker. Just put asleep 5aftermodprobe. You know you want it. It’s painless.
— The Gnome
This post lives because this time the gnome has lost. I could see three outputs for the Tailscale service when running tailscale status:
Stopped
failed to connect to local tailscaled; it doesn’t appear to be running
Starting
# Health check:
# - Tailscale is starting. Please wait.unexpected state: NoState
Started
192.168.0.0 hostname owner OS status
next IP […]
There may be multiple solutions possible, but the one that worked best for me is as follows:
#!/bin/sh
exec 2>&1
[ -r conf ] && . ./conf
modprobe -q loop || exit 1
# Start logic to check if tailscaled is running
STOPPED='failed'
STARTING='starting.'
STARTED=0
while [ $STARTED -eq 0 ]; do
STATUS="$(tailscale status 2>&1)"
if [ "$(echo "$STATUS" | awk '{printf $1}')" = "$STOPPED" ]; then
echo "Tailscale is stopped:" "$STATUS"
elif [ "$(echo "$STATUS" | awk 'FNR == 2 {printf $5}')" = "$STARTING" ]; then
echo "Tailscale is starting:" "$STATUS"
else # yes, big bug here
echo "Tailscale is running:" "$STATUS"
STARTED=1
fi
sleep 1
done
exec chpst -o 1048576 -p 1048576 dockerd $OPTS 2>&1
There is a huge bug in the sense that I’m not checking if the service actually started or if the output is a secret fourth option. And I guess I could have just checked if it is running because that is what I’m interested in, after all. I like having the statuses in the service logs if anything ever changes in the tailscale status so I can revisit in the future if needed.
It works a charm for now.