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.

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
⚠️
Never expose the Docker API over TCP without TLS in a network-accessible environment. Anyone who can reach port 2375 has full control over your Docker daemon. Always use TLS (DOCKER_TLS_VERIFY=1) or an SSH tunnel for remote Docker access.

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:

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

How do I exclude a container from Watchtower updates?

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.

Can Watchtower monitor multiple Docker hosts?

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.

How do I use Watchtower with a private registry?

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.

Does Watchtower support rootless Docker?

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.

PS
Priya Sharma
Senior DevOps Engineer · containrrr ecosystem contributor
Priya manages Docker infrastructure for a 200-node production environment and has implemented every advanced Watchtower feature covered in this guide. She contributed the socket proxy security pattern to the containrrr community documentation.