Excluding and Ignoring Containers
One of the most-used Watchtower Docker advanced features is selective container management. You have full control over which containers Watchtower monitors using Docker labels.
Exclude specific containers (default: update all)
# Add this label to any container you want Watchtower to SKIP
services:
postgres:
image: postgres:16
labels:
- "com.centurylinklabs.watchtower.enable=false" # Always ignored
nginx:
image: nginx:stable # Will be updated normally
# (no watchtower label = updated by default)
Opt-in mode (update ONLY labeled containers)
# Set this on Watchtower to require explicit opt-in
environment:
- WATCHTOWER_LABEL_ENABLE=true
# Then label only the containers you want updated:
services:
myapp:
image: myapp:latest
labels:
- "com.centurylinklabs.watchtower.enable=true" # Will be updated
postgres:
image: postgres:16
# No label = ignored when WATCHTOWER_LABEL_ENABLE=true
Monitor-only for specific containers
# Notify but never update this container
labels:
- "com.centurylinklabs.watchtower.monitor-only=true"
Custom update notification per container
# Send update notification to a different Slack channel for this container
labels:
- "com.centurylinklabs.watchtower.enable=true"
# Container-level notification overrides aren't natively supported yet
# Use WATCHTOWER_SCOPE for multi-instance routing instead
Private Registry Authentication
Watchtower Docker private registry support covers Docker Hub (authenticated), GitHub Container Registry (ghcr.io), AWS ECR, Google Container Registry (gcr.io), GitLab Registry, and any OCI-compliant registry.
Method 1: Mount Docker credentials (recommended)
# Step 1: Log in to each registry on the Docker host
docker login # Docker Hub
docker login ghcr.io -u USERNAME --password-stdin # GitHub
docker login registry.gitlab.com # GitLab
aws ecr get-login-password | docker login --username AWS \
--password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
# Step 2: Mount the resulting config.json into Watchtower
volumes:
- /root/.docker/config.json:/config.json:ro # Contains all registry credentials
Method 2: Environment variables (simple, single registry)
environment:
- REPO_USER=myDockerHubUsername
- REPO_PASS=myDockerHubPasswordOrToken
# Per-registry format (double underscore separator):
- REPO_USER__ghcr.io=myGitHubUsername
- REPO_PASS__ghcr.io=ghp_myGitHubToken
AWS ECR (rotating credentials)
# ECR tokens expire every 12 hours. Use the credential helper:
# 1. Install amazon-ecr-credential-helper on the host
# 2. Add to ~/.docker/config.json:
{
"credHelpers": {
"123456789.dkr.ecr.us-east-1.amazonaws.com": "ecr-login"
}
}
# 3. Mount config.json + the helper binary into Watchtower
volumes:
- /root/.docker/config.json:/config.json:ro
- /usr/bin/docker-credential-ecr-login:/usr/bin/docker-credential-ecr-login
Watchtower on Multiple Docker Hosts
Watchtower connects to one Docker daemon per instance. For Watchtower multiple Docker hosts, run a separate Watchtower container per host:
# On host-1: standard Watchtower with local socket
docker run -d \
--name watchtower-host1 \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WATCHTOWER_SCOPE=host1 \
-e WATCHTOWER_NOTIFICATION_TITLE_TAG=host1 \
containrrr/watchtower
# On host-2: standard Watchtower with local socket
docker run -d \
--name watchtower-host2 \
-v /var/run/docker.sock:/var/run/docker.sock \
-e WATCHTOWER_SCOPE=host2 \
-e WATCHTOWER_NOTIFICATION_TITLE_TAG=host2 \
containrrr/watchtower
For a centralized approach (one Watchtower instance managing remote hosts), use TCP Docker API access:
# On the remote host: expose Docker API (use TLS in production!)
# /etc/docker/daemon.json:
{"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]}
# Watchtower pointing to remote host
docker run -d \
--name watchtower-remote \
-e DOCKER_HOST=tcp://192.168.1.100:2375 \
containrrr/watchtower
DOCKER_TLS_VERIFY=1) or an SSH tunnel for remote Docker access. If you run into connection issues, see the troubleshooting guide for debugging steps.Docker Socket Proxy (Security Hardening)
Mounting the raw Docker socket into Watchtower gives it root-equivalent access to the host. The Watchtower Docker socket proxy pattern limits this exposure. This is especially important in Docker Compose setups where Watchtower runs alongside application services:
services:
# Tecnativa Docker Socket Proxy — limits what Watchtower can do
socket-proxy:
image: tecnativa/docker-socket-proxy
restart: unless-stopped
privileged: true
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- CONTAINERS=1 # Allow reading container info
- IMAGES=1 # Allow reading/pulling images
- POST=1 # Allow POST requests (needed for updates)
- DELETE=0 # Disallow delete (cleanup still works via Watchtower's own logic)
networks:
- socket-proxy
watchtower:
image: containrrr/watchtower
restart: unless-stopped
environment:
- DOCKER_HOST=tcp://socket-proxy:2375 # Connect via proxy, not raw socket
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_SCHEDULE=0 0 4 * * *
networks:
- socket-proxy
# No socket volume mount needed — connects via TCP to proxy
networks:
socket-proxy:
driver: bridge
internal: true # Isolated from external networks
Rootless Docker Support
In rootless Docker mode, the socket path differs from the default. Configure Watchtower Docker rootless setup:
# Find your rootless socket path:
echo $DOCKER_HOST
# Typically: unix:///run/user/1000/docker.sock
# Watchtower with rootless Docker (environment variable, not volume mount):
docker run -d \
--name watchtower \
-e DOCKER_HOST=unix:///run/user/1000/docker.sock \
-v /run/user/1000/docker.sock:/var/run/docker.sock \
containrrr/watchtower
# In Docker Compose:
services:
watchtower:
image: containrrr/watchtower
environment:
- DOCKER_HOST=unix:///var/run/docker.sock
volumes:
- /run/user/1000/docker.sock:/var/run/docker.sock
Zero-Downtime Update Strategies
Watchtower Docker zero downtime updates are achievable with a combination of rolling restarts and health-check-aware pre/post update hooks.
# Rolling restart: update one container at a time (prevents all replicas restarting simultaneously)
environment:
- WATCHTOWER_ROLLING_RESTART=true
# Per-container: pre/post update lifecycle hooks
labels:
# Run before Watchtower stops the container (drain traffic, save state)
- "com.centurylinklabs.watchtower.lifecycle.pre-update=/scripts/pre-update.sh"
# Run after container is updated and running (notify load balancer)
- "com.centurylinklabs.watchtower.lifecycle.post-update=/scripts/post-update.sh"
# Example pre-update script (inside the container)
#!/bin/sh
# Signal the application to stop accepting new requests
kill -SIGTERM 1
sleep 5 # Allow in-flight requests to complete
Multi-Instance Scoping
Run multiple Watchtower instances on the same Docker host, each managing a different set of containers:
services:
# Instance 1: manages "production" containers with longer interval
watchtower-prod:
image: containrrr/watchtower
environment:
- WATCHTOWER_SCOPE=production
- WATCHTOWER_SCHEDULE=0 0 2 * * 0 # Weekly, 2 AM Sunday
- WATCHTOWER_CLEANUP=true
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# Instance 2: manages "dev" containers with frequent checks
watchtower-dev:
image: containrrr/watchtower
environment:
- WATCHTOWER_SCOPE=development
- WATCHTOWER_POLL_INTERVAL=3600 # Every hour
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# Each container declares which scope manages it:
# labels:
# - "com.centurylinklabs.watchtower.scope=production"
# # OR
# - "com.centurylinklabs.watchtower.scope=development"
Frequently Asked Questions
Add the label com.centurylinklabs.watchtower.enable=false to the container. Watchtower will skip it during all future update checks. This works regardless of whether WATCHTOWER_LABEL_ENABLE is set — the disable label is always honored.
Each Watchtower instance connects to one Docker daemon. To monitor multiple hosts, run a Watchtower container on each host, or run separate Watchtower instances pointing to remote hosts via DOCKER_HOST=tcp://host:2375. Use WATCHTOWER_SCOPE and WATCHTOWER_NOTIFICATION_TITLE_TAG to distinguish notifications from different hosts.
Run docker login registry.example.com on the host, then mount /root/.docker/config.json:/config.json:ro into Watchtower. One config.json file stores credentials for all registries you've logged into. For AWS ECR (rotating credentials), install the amazon-ecr-credential-helper and reference it in config.json.
Yes. In rootless Docker, the socket is at /run/user/UID/docker.sock. Mount it into Watchtower at /var/run/docker.sock, or set DOCKER_HOST=unix:///run/user/1000/docker.sock. The rest of the configuration is identical to standard Docker.