Automatic Docker Container Updates
The Container Update Problem
Docker containers don’t update themselves. When the maintainer publishes a new image, your running container stays on the old version. You need to manually pull the new image and recreate the container. For one or two services, that’s manageable. For 20+, it’s a maintenance burden.
This guide covers automatic update solutions, when to use them, and when not to.
Prerequisites
- Docker and Docker Compose installed (Docker Compose Basics)
- Understanding of Docker images and tags (Docker Image Management)
- Terminal access to your server (SSH Setup)
Should You Auto-Update?
Automatic updates are convenient but come with risk. A broken update at 3 AM with no one watching can take down services.
Good candidates for auto-update:
- Non-critical services (dashboard, link pages, speedtest)
- Services that follow semantic versioning and rarely break
- Services where you pin to a major version tag (e.g.,
postgres:16)
Bad candidates for auto-update:
- Databases (PostgreSQL, MariaDB) — major version updates require migration
- Services with complex configs that break between versions
- Business-critical services where downtime matters
Recommended approach: Auto-update most services, but exclude databases and critical services. Monitor notifications so you know what changed.
Watchtower
Watchtower is deprecated. The
containrrr/watchtowerrepository is archived and no longer maintained. For new setups, use DIUN (notify-only, never touches containers) or What’s Up Docker (actively maintained, supports manual approval). The configuration below is kept for reference if you have an existing Watchtower installation.
Watchtower was the most popular automatic update tool for Docker containers. It monitors running containers, checks for new image versions, pulls them, and recreates containers with the same configuration.
Basic Setup
services:
watchtower:
image: containrrr/watchtower:1.7.1
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_CLEANUP: "true" # Remove old images
WATCHTOWER_SCHEDULE: "0 0 4 * * *" # Run at 4 AM daily
WATCHTOWER_TIMEOUT: 30s
Monitor Only (No Automatic Updates)
If you want notifications without automatic updates:
services:
watchtower:
image: containrrr/watchtower:1.7.1
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_MONITOR_ONLY: "true"
WATCHTOWER_SCHEDULE: "0 0 4 * * *"
WATCHTOWER_NOTIFICATIONS: email
WATCHTOWER_NOTIFICATION_EMAIL_FROM: [email protected]
WATCHTOWER_NOTIFICATION_EMAIL_TO: [email protected]
WATCHTOWER_NOTIFICATION_EMAIL_SERVER: smtp.example.com
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT: "587"
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER: [email protected]
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD: your-smtp-password
Exclude Specific Containers
Label containers you don’t want updated:
services:
postgres:
image: postgres:16.2
labels:
- "com.centurylinklabs.watchtower.enable=false"
Include Only Specific Containers
Alternatively, only update explicitly labeled containers:
services:
watchtower:
image: containrrr/watchtower:1.7.1
environment:
WATCHTOWER_LABEL_ENABLE: "true" # Only update labeled containers
# This container WILL be updated
nextcloud:
image: nextcloud:29.0.0
labels:
- "com.centurylinklabs.watchtower.enable=true"
# This container will NOT be updated (no label)
postgres:
image: postgres:16.2
Notification Options
Watchtower supports multiple notification channels:
environment:
# Email
WATCHTOWER_NOTIFICATIONS: email
# Slack
WATCHTOWER_NOTIFICATIONS: slack
WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL: "https://hooks.slack.com/services/xxx"
# Gotify (self-hosted)
WATCHTOWER_NOTIFICATIONS: gotify
WATCHTOWER_NOTIFICATION_GOTIFY_URL: "http://gotify:80"
WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN: "your-token"
# Generic webhook
WATCHTOWER_NOTIFICATIONS: shoutrrr
WATCHTOWER_NOTIFICATION_URL: "discord://token@channel"
Manual Update Workflow
When you don’t use auto-updates, here’s the efficient manual process:
# Pull latest images for all services
docker compose pull
# Check what changed
docker compose ps # Note current image IDs
docker images # See newly pulled images
# Recreate containers with new images
docker compose up -d
# Verify everything is working
docker compose ps
docker compose logs --tail=20
Update Script
#!/bin/bash
# update.sh — Update all Docker Compose stacks
COMPOSE_DIRS=(
"/opt/reverse-proxy"
"/opt/nextcloud"
"/opt/jellyfin"
"/opt/monitoring"
)
for dir in "${COMPOSE_DIRS[@]}"; do
echo "=== Updating $dir ==="
cd "$dir" || continue
docker compose pull
docker compose up -d
echo ""
done
# Clean up old images
docker image prune -f
echo "=== Update complete ==="
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
See Docker Updating for the complete manual update process.
Rollback Strategy
Before updating, know how to roll back.
With Pinned Tags
If you pin version tags (recommended), rolling back is straightforward:
# Before update
image: nextcloud:28.0.0
# After update (change the tag)
image: nextcloud:29.0.0
# To rollback (change back)
image: nextcloud:28.0.0
docker compose up -d # Recreate with the old version
Without Pinned Tags
If you used a floating tag like :latest, the old image may have been deleted during cleanup. This is why pinning versions matters.
# Check if the old image still exists
docker images myapp
# If gone, you can't easily roll back
# This is why you should always pin versions
Database Rollbacks
Database rollbacks are more complex because data may have been migrated.
# BEFORE updating a database, create a backup
docker exec postgres pg_dumpall -U postgres > /backup/db-$(date +%Y%m%d).sql
# If the update breaks things, restore
docker compose down
docker volume rm myproject_postgres_data
docker compose up -d postgres
docker exec -i postgres psql -U postgres < /backup/db-YYYYMMDD.sql
See Backup Docker Volumes for backup strategies.
Update Schedule Recommendations
| Category | Update Frequency | Method |
|---|---|---|
| Security-critical (reverse proxy, auth) | Weekly | Manual or auto with notifications |
| Standard web apps | Weekly to monthly | Auto with DIUN notifications or What’s Up Docker |
| Databases | Monthly, after backup | Manual only |
| Monitoring tools | Monthly | Auto with DIUN notifications or What’s Up Docker |
| Media servers | Monthly | Auto or manual |
Cron-Based Manual Updates
If you prefer manual updates on a schedule:
# /etc/cron.d/docker-updates
# Check for updates every Sunday at 3 AM, send email with results
0 3 * * 0 root /opt/scripts/check-updates.sh | mail -s "Docker Update Report" [email protected]
#!/bin/bash
# check-updates.sh — Check for available updates (don't apply)
echo "Docker Image Update Check — $(date)"
echo "================================"
for dir in /opt/*/docker-compose.yml; do
dir=$(dirname "$dir")
cd "$dir" || continue
echo -e "\n--- $dir ---"
docker compose pull --dry-run 2>&1 || docker compose pull 2>&1 | grep -v "up to date"
done
FAQ
Is Watchtower safe to use in production?
Watchtower is now deprecated — the containrrr/watchtower repository has been archived. It was widely used but carried inherent risk, as any automatic update can break things. For new setups, use DIUN (notify-only) or What’s Up Docker (actively maintained with manual approval support). If you have an existing Watchtower installation, it will continue to function but will not receive updates or security patches.
Why shouldn’t I use :latest tags with auto-updates?
:latest is a moving target. Watchtower will pull whatever :latest points to, which could be a major version bump with breaking changes. With pinned tags like nextcloud:29, Watchtower only updates within that version (patch releases), which are usually safe.
Can Watchtower update itself?
Yes. Watchtower monitors its own container by default. You can disable this with WATCHTOWER_INCLUDE_STOPPED=false or by labeling it with com.centurylinklabs.watchtower.enable=false.
What about Docker Compose file changes?
Watchtower only updates images — it doesn’t modify your docker-compose.yml. If a new version requires new environment variables or volume mounts, Watchtower will pull the image but the container may fail with the old config. For these cases, manual updates with config changes are required.
Related
Get self-hosting tips in your inbox
New guides, comparisons, and setup tutorials — delivered weekly. No spam.