
How to Automatically Update Docker Container Images with Watchtower on Ubuntu 24
Managing Docker containers manually is about as fun as watching paint dry, especially when you’re dealing with production environments that need regular updates. Watchtower automates the entire process of updating Docker container images, monitoring your running containers for newer versions and seamlessly replacing them without breaking your deployment flow. This guide walks you through setting up Watchtower on Ubuntu 24, configuring automatic updates, handling rollbacks, and avoiding the common gotchas that can turn your smooth automation into a debugging nightmare.
How Watchtower Works Under the Hood
Watchtower operates as a Docker container itself, which is pretty clever when you think about it. It connects to the Docker daemon via the Docker socket and periodically polls container registries to check for updated images. When it finds a newer version, Watchtower pulls the new image, gracefully stops the existing container, and starts a replacement with the same configuration.
The polling mechanism uses image digests and tags to determine if updates are available. By default, Watchtower checks every 24 hours, but you can configure this interval based on your needs. It maintains the original container’s environment variables, port mappings, volumes, and network settings, making the update process transparent to your applications.
Here’s what happens during a typical update cycle:
- Watchtower queries the registry for the latest image digest
- Compares the digest with the currently running container’s image
- Downloads the new image if a difference is detected
- Stops the old container gracefully (sends SIGTERM, waits for cleanup)
- Creates and starts a new container with identical configuration
- Optionally removes the old image to save disk space
Step-by-Step Setup Guide for Ubuntu 24
First, make sure Docker is properly installed and your user has appropriate permissions. If you haven’t set this up yet, here’s the quick version:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
newgrp docker
Now let’s deploy Watchtower. The basic setup requires mounting the Docker socket so Watchtower can communicate with the Docker daemon:
docker run -d \
--name watchtower \
--restart unless-stopped \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
This gives you the default behavior: checking all containers every 24 hours. For production environments, you’ll want more control. Here’s a more comprehensive configuration:
docker run -d \
--name watchtower \
--restart unless-stopped \
-e WATCHTOWER_POLL_INTERVAL=3600 \
-e WATCHTOWER_CLEANUP=true \
-e WATCHTOWER_INCLUDE_RESTARTING=true \
-e WATCHTOWER_NOTIFICATIONS_LEVEL=info \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
For even better control, create a docker-compose.yml file:
version: '3.8'
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
environment:
- WATCHTOWER_POLL_INTERVAL=3600
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_INCLUDE_RESTARTING=true
- WATCHTOWER_NOTIFICATIONS_LEVEL=info
- WATCHTOWER_TIMEOUT=30s
- WATCHTOWER_ROLLING_RESTART=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 3600 --cleanup
Deploy it with:
docker-compose up -d
Configuration Options and Fine-Tuning
Watchtower offers extensive configuration through environment variables and command-line arguments. Here are the most useful ones for real-world deployments:
Environment Variable | Description | Default Value | Use Case |
---|---|---|---|
WATCHTOWER_POLL_INTERVAL | Check interval in seconds | 86400 (24h) | Frequent updates for dev, longer for prod |
WATCHTOWER_CLEANUP | Remove old images after update | false | Prevent disk space issues |
WATCHTOWER_TIMEOUT | Stop timeout for containers | 10s | Graceful shutdown for databases |
WATCHTOWER_ROLLING_RESTART | Update containers one by one | false | Maintain service availability |
WATCHTOWER_INCLUDE_STOPPED | Update stopped containers | false | Keep all images current |
You can also target specific containers using labels. Add this label to containers you want Watchtower to monitor:
docker run -d \
--label com.centurylinklabs.watchtower.enable=true \
--name myapp \
nginx:latest
Then configure Watchtower to only watch labeled containers:
docker run -d \
--name watchtower \
--restart unless-stopped \
-e WATCHTOWER_LABEL_ENABLE=true \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
Real-World Use Cases and Examples
Here are some practical scenarios where Watchtower shines:
Development Environment Auto-Updates: Set up a short polling interval for development containers that need to stay current with the latest builds:
version: '3.8'
services:
app:
image: myregistry/myapp:latest
labels:
- com.centurylinklabs.watchtower.enable=true
ports:
- "3000:3000"
watchtower:
image: containrrr/watchtower
environment:
- WATCHTOWER_POLL_INTERVAL=300 # 5 minutes
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_LABEL_ENABLE=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Production Security Updates: Configure Watchtower to only update containers with security patches, using specific tag patterns:
docker run -d \
--name watchtower-security \
--restart unless-stopped \
-e WATCHTOWER_POLL_INTERVAL=7200 \
-e WATCHTOWER_CLEANUP=true \
-e WATCHTOWER_ROLLING_RESTART=true \
-e WATCHTOWER_timeout=60s \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower \
--include-restarting \
container1 container2 container3
Notification Integration: Set up Slack notifications for production updates:
docker run -d \
--name watchtower \
--restart unless-stopped \
-e WATCHTOWER_NOTIFICATIONS=slack \
-e WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=https://hooks.slack.com/services/... \
-e WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER=watchtower-prod \
-e WATCHTOWER_NOTIFICATION_SLACK_CHANNEL=#deployments \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
Comparison with Alternative Solutions
Watchtower isn’t the only game in town for container updates. Here’s how it stacks up against other options:
Solution | Complexity | Features | Best For | Limitations |
---|---|---|---|---|
Watchtower | Low | Automatic updates, notifications, filtering | Simple deployments, dev environments | No rollback strategy, limited orchestration |
Portainer | Medium | GUI management, manual updates, stacks | Manual control, visual management | No automatic updates without webhooks |
Kubernetes | High | Rolling updates, rollbacks, health checks | Production clusters, complex applications | Steep learning curve, resource overhead |
Custom CI/CD | High | Full customization, testing integration | Complex workflows, enterprise environments | Requires significant setup and maintenance |
For most single-server deployments and development environments, Watchtower provides the best balance of simplicity and functionality. It’s particularly useful when you’re running Docker Compose applications and want automatic updates without the complexity of a full orchestration platform.
Best Practices and Security Considerations
Running Watchtower safely requires following several important practices:
Registry Authentication: For private registries, you’ll need to provide credentials. The cleanest approach uses a config.json file:
mkdir -p ~/.docker
echo '{
"auths": {
"your-registry.com": {
"auth": "base64-encoded-credentials"
}
}
}' > ~/.docker/config.json
docker run -d \
--name watchtower \
--restart unless-stopped \
-v ~/.docker/config.json:/config.json \
-e DOCKER_CONFIG=/config.json \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
Resource Limits: Always set resource constraints to prevent Watchtower from consuming excessive resources during image pulls:
docker run -d \
--name watchtower \
--restart unless-stopped \
--memory=512m \
--cpus=0.5 \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
Backup Strategy: Before implementing automatic updates, ensure you have a solid backup and rollback strategy:
#!/bin/bash
# Pre-update backup script
BACKUP_DIR="/backups/$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR
# Export container configurations
docker ps --format "table {{.Names}}\t{{.Image}}" > $BACKUP_DIR/running_containers.txt
# Backup volumes
docker run --rm -v myapp_data:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/myapp_data.tar.gz -C /data .
echo "Backup completed: $BACKUP_DIR"
Health Check Integration: Use health checks to ensure containers are actually running properly after updates:
version: '3.8'
services:
app:
image: myapp:latest
labels:
- com.centurylinklabs.watchtower.enable=true
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
ports:
- "3000:3000"
Common Issues and Troubleshooting
Here are the most frequent problems you’ll encounter and their solutions:
Permission Issues: If Watchtower can’t access the Docker socket, you’ll see permission errors. Fix this by ensuring proper socket permissions:
# Check current permissions
ls -la /var/run/docker.sock
# Fix permissions if needed
sudo chmod 666 /var/run/docker.sock
# Better: add user to docker group
sudo usermod -aG docker $USER
Memory Issues During Updates: Large image pulls can cause out-of-memory errors. Monitor disk space and set up cleanup:
# Check disk usage
docker system df
# Clean up unused images
docker system prune -a
# Set up automatic cleanup in Watchtower
-e WATCHTOWER_CLEANUP=true
Update Failures: When containers fail to start after updates, check the logs:
# Check Watchtower logs
docker logs watchtower --tail 50
# Check the updated container logs
docker logs container_name --tail 50
# Roll back manually if needed
docker stop container_name
docker run -d --name container_name previous_image:tag
Registry Rate Limits: Docker Hub and other registries impose rate limits. If you’re hitting these, implement caching or use authentication:
# Use Docker Hub authentication to increase limits
docker login
# Or set up a local registry cache
docker run -d -p 5000:5000 --name registry-cache \
-e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \
registry:2
Network Connectivity Issues: Watchtower needs internet access to check for updates. Test connectivity and configure proxy settings if needed:
# Test registry connectivity
docker run --rm alpine/curl curl -I https://registry-1.docker.io/v2/
# Configure HTTP proxy for Watchtower
-e HTTP_PROXY=http://proxy.company.com:8080 \
-e HTTPS_PROXY=http://proxy.company.com:8080
Advanced Configuration and Monitoring
For production environments, you’ll want more sophisticated monitoring and control. Here’s how to set up comprehensive logging and monitoring:
version: '3.8'
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
environment:
- WATCHTOWER_POLL_INTERVAL=3600
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_INCLUDE_RESTARTING=true
- WATCHTOWER_NOTIFICATIONS=slack
- WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=${SLACK_WEBHOOK}
- WATCHTOWER_NOTIFICATION_SLACK_CHANNEL=#infrastructure
- WATCHTOWER_DEBUG=true
- WATCHTOWER_TRACE=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./watchtower-logs:/logs
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
You can also integrate Watchtower with monitoring tools like Prometheus by parsing its logs or using webhook notifications:
# Webhook notification example
-e WATCHTOWER_NOTIFICATIONS=webhook \
-e WATCHTOWER_NOTIFICATION_WEBHOOK_URL=http://monitoring.local/webhook/watchtower \
-e WATCHTOWER_NOTIFICATION_WEBHOOK_TEMPLATE='{"text":"Container {{.Name}} updated from {{.OldImage}} to {{.NewImage}}"}'
For more advanced use cases, check out the official Watchtower documentation at https://containrrr.dev/watchtower/ and the Docker documentation at https://docs.docker.com/ for additional configuration options and best practices.
Remember that automation is great, but it’s not a substitute for proper testing and deployment practices. Always test updates in a staging environment first, maintain proper backups, and have a rollback plan ready. Watchtower works best as part of a comprehensive deployment strategy, not as a fire-and-forget solution.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.