I run ZeroTier in a Docker container on a Synology DS124 with DSM 7.3. After DSM updates or reboots, the ZeroTier layer repeatedly failed to return even though the container had a restart policy.
Symptoms
The container remained stopped:
Status: Exited (143)
Restart policy: unless-stopped
Docker retained this error:
error gathering device information while adding custom device
"/dev/net/tun": no such file or directory
The ZeroTier interfaces were missing, and the NAS was no longer reachable through the overlay network.
Root cause
This turned out to be a boot-order problem:
- DSM stops Container Manager cleanly during an update or shutdown.
/dev/net/tun is created dynamically during boot.
- Container Manager may try to restore the ZeroTier container before the TUN device is ready.
- The container fails or remains stopped.
unless-stopped does not reliably recover a container that DSM explicitly stopped.
The ZeroTier data itself was not lost because it was stored persistently outside the container.
Final fix
I used three layers:
- Change the restart policy:
docker update --restart always zerotier-one
- Add a DSM boot-triggered task that:
- waits for
/dev/net/tun;
- creates it if DSM does not;
- waits for Docker;
- starts the container;
- confirms ZeroTier reaches
ONLINE;
- abandons recovery after 90 seconds so it cannot block DSM boot.
- Add a periodic watchdog task that performs the same checks and exits silently when everything is healthy.
The boot task is configured in:
Control Panel
→ Task Scheduler
→ Create
→ Triggered Task
→ User-defined script
→ Event: Boot-up
→ User: root
The watchdog is a normal scheduled task running every five minutes.
Minimal recovery script
This is the shortened version. The fuller version adds locking, DSM Log Center integration and hard global deadlines.
#!/bin/sh
CONTAINER="zerotier-one"
# Give DSM time to create TUN.
WAITED=0
while [ ! -c /dev/net/tun ] && [ "$WAITED" -lt 30 ]; do
sleep 5
WAITED=$((WAITED + 5))
done
# Repair TUN if DSM did not create it.
if [ ! -c /dev/net/tun ]; then
modprobe tun 2>/dev/null || true
mkdir -p /dev/net
chmod 755 /dev/net
rm -f /dev/net/tun
mknod /dev/net/tun c 10 200 || exit 1
fi
chown root:root /dev/net/tun
chmod 666 /dev/net/tun
# Wait for Docker.
WAITED=0
while ! docker info >/dev/null 2>&1 && [ "$WAITED" -lt 60 ]; do
sleep 5
WAITED=$((WAITED + 5))
done
docker info >/dev/null 2>&1 || exit 1
# Start the container if required.
if ! docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
docker start "$CONTAINER" || exit 1
fi
# Confirm ZeroTier responds.
WAITED=0
while ! docker exec "$CONTAINER" zerotier-cli info >/dev/null 2>&1 \
&& [ "$WAITED" -lt 30 ]; do
sleep 5
WAITED=$((WAITED + 5))
done
docker exec "$CONTAINER" zerotier-cli info
docker exec "$CONTAINER" zerotier-cli listnetworks
Validation
After reboot, the boot task logged that /dev/net/tun was initially absent, waited, started the container and completed recovery after approximately 39 seconds.
And all work back.