Self-hosted apps receive security fixes and new features through new image versions. Watchtower watches your running containers, checks their registries for newer images with the same tag, and recreates the containers with the same settings when one appears.
It’s convenient — and risky if used carelessly. Here’s how to set it up sensibly.
The basic setup
# compose.yaml
services:
watchtower:
image: containrrr/watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_SCHEDULE: "0 0 4 * * *" # 04:00 daily (6-field cron, with seconds)
WATCHTOWER_CLEANUP: "true" # remove old images after updating
TZ: Europe/London
docker compose up -d
docker logs -f watchtower
Watchtower needs the Docker socket — which means it has root-equivalent control of the host. Use the official image and keep it updated.
The original Watchtower project has seen reduced maintenance; check its status and consider maintained forks or alternatives (below) before relying on it.
Only update what you choose: labels
Updating everything automatically is the main risk. Opt in per container instead:
environment:
WATCHTOWER_LABEL_ENABLE: "true"
Then label only the containers that should auto-update:
services:
uptime-kuma:
image: louislam/uptime-kuma:1
labels:
- com.centurylinklabs.watchtower.enable=true
Or the opposite: update everything except labelled containers (com.centurylinklabs.watchtower.enable=false).
Monitor-only mode
Get notified about new images without updating:
WATCHTOWER_MONITOR_ONLY: "true"
Combine it with notifications and update manually after reading release notes. For databases and critical apps, this is the safer choice.
Notifications
Watchtower uses the shoutrrr library for notifications (email, Telegram, Slack, Discord, ntfy, Gotify and more):
WATCHTOWER_NOTIFICATION_URL: "telegram://BOT_TOKEN@telegram?chats=CHAT_ID"
Or smtp://user:pass@host:587/[email protected]&[email protected].
Tags matter more than Watchtower
Watchtower updates within a tag. The tag you choose decides how big an update can be:
| Tag | Updates you’ll get |
|---|---|
latest |
Anything, including major versions with breaking changes |
1 |
Minor and patch releases within major version 1 |
1.4 |
Patch releases only |
1.4.2 |
Nothing (pinned) |
For apps that follow semantic versioning, a major-version tag (postgres:17, uptime-kuma:1) with Watchtower gives security patches without surprise major upgrades.
When not to auto-update
- Databases (PostgreSQL, MySQL, MongoDB): major upgrades need data migrations; even minor upgrades deserve a backup first.
- Apps with migration steps or frequent breaking changes (read their upgrade notes).
- Anything you can’t afford to break at 4 a.m. without someone watching.
A middle ground: auto-update low-risk utilities (dashboards, monitoring tools, small stateless apps), monitor-only for the rest.
Backups before updates
Updates can corrupt data or fail midway. Back up volumes before the update window — e.g. a backup cron job at 03:00 and Watchtower at 04:00. See backing up Docker volumes.
Alternatives
- Manual updates with
docker compose pull && docker compose up -dafter reading release notes — still the safest for important services. See updating self-hosted apps. - Diun — notifies about new image versions without touching containers.
- Renovate — opens pull requests that bump image tags in your Compose files in Git, so updates are reviewed and versioned.
- Podman auto-update — built into Podman with systemd.
Run once
To update everything right now (and exit):
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower --run-once
Related: Docker Compose tutorial and docker prune. A Docker VPS running a dozen self-hosted apps is exactly where a careful Watchtower setup saves time.