Auto-Update Containers with Watchtower
30 Sep 2026Watchtower watches your running containers and pulls new images the moment they’re pushed. No cron job, no manual docker pull && docker up -d. It also means a bad upstream release lands on your server at 3am with nobody watching.
What it actually does
Watchtower polls the registry for each container’s image tag on an interval. If the digest changed, it pulls the new image, stops the old container, and starts a new one with the same config. That’s it — no build step, no test run, no approval gate.
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower --interval 3600
That one line auto-updates every container on the host, every hour, forever.
The 3am failure mode
You’re running postgres:16 with no digest pin. Upstream ships 16.4 with a changed default for max_connections, or a security patch that breaks an extension you depend on. Watchtower pulls it, restarts the container, and your app starts throwing connection errors while you’re asleep.
The failure isn’t Watchtower’s fault — it’s doing exactly what you told it to. The mistake is auto-updating anything that holds state or matters at 2am.
Scope it with labels, not a blanket interval
Don’t run the bare command above. Opt containers in explicitly:
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower --interval 3600 --label-enable
Then tag only the containers you actually want auto-updated:
services:
nginx-proxy:
image: nginx:1.27
labels:
- "com.centurylinklabs.watchtower.enable=true"
postgres:
image: postgres:16
# no label — Watchtower ignores this one
Stateless, easily-restarted services (reverse proxies, static site containers, a status page) are good candidates. Databases, queues, and anything with migrations are not.
Pin tags, don’t float on latest
image: myapp:latest means Watchtower updates on any push, including ones you didn’t mean to ship broadly. Use a specific minor version (myapp:1.4) so a 1.4.x patch release is the only thing that moves under you, and bump major/minor versions yourself, deliberately.
Get notified before you get paged
Watchtower can post to Slack, Discord, or a generic webhook on every update:
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WATCHTOWER_NOTIFICATIONS=slack \
-e WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=https://hooks.slack.com/services/XXX \
containrrr/watchtower --interval 3600 --label-enable
Now an update at 3am shows up in Slack instead of only in docker logs watchtower, and you can correlate “site’s down” with “oh, it updated an hour ago” in seconds instead of guessing.
What breaks and how to see it
| Symptom | Likely cause | Fix |
|---|---|---|
| Container restarts, app 500s | New image changed a default (env var, config format) | Pin the tag, read the changelog before bumping |
| Container won’t start after update | Image removed a feature you relied on | docker logs <container>, roll back tag manually |
| Data loss / corruption on restart | Auto-updated a stateful service mid-write | Never label stateful containers for auto-update |
If a container gets stuck in a bad state, Watchtower doesn’t roll back for you — it only rolls forward. Keep the previous image tag noted somewhere (a comment in your compose file is enough) so docker run myapp:1.3 is a thirty-second fix, not an archaeology project.
Use Watchtower for the boring, stateless stuff. Update everything else on your own schedule, when you’re awake to watch it.