Twelve containers on :latest means twelve things quietly going stale. Watchtower watches for new
images, pulls them, and recreates the container on your schedule. It’s the laziest possible update strategy.
The Setup
Watchtower rides in the same compose.yml as
the rest of the fleet.
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: ${RESTART_POLICY}
environment:
- TZ=${TZ}
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_LABEL_ENABLE=true
- 'WATCHTOWER_SCHEDULE=${WATCHTOWER_SCHEDULE}'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Handing a container /var/run/docker.sock is effectively giving it root on the host — it can start, stop
and recreate anything. That’s the deal with Watchtower.
Gated Upgrades
By default Watchtower updates every container on the host. The fix is label-gating
with WATCHTOWER_LABEL_ENABLE=true and Watchtower only touches containers wearing
the opt-in label:
labels:
- 'com.centurylinklabs.watchtower.enable=true'
Every service in the compose file carries that line, so a stray container on the same host won’t get a surprise upgrade.
Scheduling and cleanup
WATCHTOWER_SCHEDULE takes a 6-field cron (the leading field is seconds — a Watchtower quirk, not
standard cron). Pick a quiet hour so a recreate never lands mid-stream:
# At 04:00 every day
WATCHTOWER_SCHEDULE=0 0 4 * * *
# Every 4th of the month at 05:00
WATCHTOWER_SCHEDULE=0 0 5 4 * *
WATCHTOWER_CLEANUP=true bins the old image after a successful update.
Notifications
An update you don’t hear about is an update you can’t correlate with “why did Prowlarr break last
Tuesday”. Watchtower can ping you whenever it recreates something. The full
.env-example has the notification block —
left blank in the compose walkthrough — ready
for Slack or e-mail:
environment:
# Slack (or any shoutrrr URL: discord://, telegram://, ...)
- WATCHTOWER_NOTIFICATIONS=slack
- WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=
- WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER=Watchtower
# or email
- WATCHTOWER_NOTIFICATIONS=email
- WATCHTOWER_NOTIFICATION_EMAIL_FROM=
- WATCHTOWER_NOTIFICATION_EMAIL_TO=
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER=
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=
- WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=
The Danger
Watchtower is the lazy option, not the safe one. :latest is a
moving target, and “pull whatever’s newest and recreate” will, eventually, pull a release that changes a
config format, drops a setting, or just plain breaks — and it’ll do it at 4am while you’re asleep, with no
“are you sure”.
That’s a fine bargain for a home media stack: if Sonarr is down for a morning, nobody files a ticket. It’s a terrible bargain for anything you actually depend on. So the real decision is per-fleet:
- Low-stakes, high-churn (the *arr apps, dashboards) → let Watchtower keep them fresh. The whole point is to never think about it.
-
Load-bearing or stateful (databases, your reverse proxy) → don’t label but update on your own terms:
docker compose pull && docker compose up -d
Alternative
If auto-recreate makes you nervous consider Renovate / Dependabot — the grown-up option where you pin tags and update with an automatically created pull request.
Conclusion
Auto-updates are a convenience, not a safety feature. Know which of your containers can afford a bad morning, and handle the rest manually. 🏴☠️