Watchtower auto-updates running containers when a new image is pushed to the registry. The original containrrr/watchtower is effectively unmaintained — nickfedor/watchtower is the active fork, and what I deploy everywhere now.
Two deploys below: one-line docker run for ad-hoc hosts, and a docker-compose.yml for anything that lives long enough to belong in a compose file.
One-line `docker run`
Polls every 5 minutes (-i 300) and removes the old image after a successful pull (--cleanup):
docker run -d \
--restart always \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
nickfedor/watchtower:latest \
-i 300 --cleanup
That's the whole thing. Everything below is iterations on the same shape — same image, same socket mount, different flags or scoping.
Docker Compose
Drop into docker-compose.yml and docker compose up -d:
services:
watchtower:
image: nickfedor/watchtower:latest
container_name: watchtower
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 300 --cleanup
Long-form flags (--interval, --cleanup) read better in a compose file than the short forms. Same behaviour as the docker run above.
Same Compose file, with a few flags I actually use
Most of my hosts run something close to this:
services:
watchtower:
image: nickfedor/watchtower:latest
container_name: watchtower
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
TZ: America/Los_Angeles
WATCHTOWER_POLL_INTERVAL: 300
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_INCLUDE_RESTARTING: "true"
WATCHTOWER_LABEL_ENABLE: "true"
WATCHTOWER_ROLLING_RESTART: "true"
WATCHTOWER_NOTIFICATIONS: shoutrrr
WATCHTOWER_NOTIFICATION_URL: "discord://token@channel"
Why each of these:
WATCHTOWER_LABEL_ENABLE=true— opt-in mode. Watchtower only touches containers withcom.centurylinklabs.watchtower.enable=true. Safer default than letting it manage everything on the host.WATCHTOWER_ROLLING_RESTART=true— restarts containers one at a time instead of stopping all updateable containers, pulling, and starting them back. Avoids a brief "everything is down" window.WATCHTOWER_CLEANUP=true— equivalent to--cleanup; removes the dangling old image after a successful update.WATCHTOWER_NOTIFICATIONS=shoutrrr— pipes update events through shoutrrr; URL format covers Discord, Slack, Telegram, Pushover, generic webhook, etc.
Opt-in label on the containers you want updated
When WATCHTOWER_LABEL_ENABLE=true, every container that should be auto-updated needs the label:
services:
myapp:
image: ghcr.io/me/myapp:latest
labels:
com.centurylinklabs.watchtower.enable: "true"
Anything without the label is ignored. Pin the image tag explicitly when you don't want auto-updates (e.g. postgres:16.4, not postgres:latest) — Watchtower only updates when the digest behind a tag changes, so a pinned tag is naturally pinned in place.
Run-once mode (useful for cron / CI)
If you'd rather drive updates from an external scheduler than have a long-running watcher, run Watchtower one-shot:
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
nickfedor/watchtower:latest \
--run-once --cleanup
Pair with a systemd timer or cron job. Useful when you want updates to happen at a specific time (3 AM maintenance window) instead of on a polling interval.
Worth knowing
- Mounting the Docker socket is root-on-host. Anything inside the Watchtower container can control Docker, which is equivalent to root on the host. Don't run untrusted images this way, and don't expose the socket over TCP.
- Image digest, not tag, is what triggers an update. If you
docker pull myimage:latestand the digest is unchanged, Watchtower does nothing. This is the right behaviour but trips people up when they expect a "force restart" from:latest. --cleanuponly removes the old image that the container was using before the update. It doesn't garbage-collect unrelated dangling images — usedocker image prunefor that.- Self-update works. Watchtower will update its own container; it shuts itself down, the new container takes over with the same config. No special handling needed.
- Don't run two Watchtowers against the same containers. They'll race each other restarting things. One per host (or use label scoping if you genuinely need multiple instances watching different sets).
References
- nickfedor/watchtower — active fork, current upstream
- Watchtower arguments reference — full flag list
- Shoutrrr URL format — for notification URLs