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
⚠️
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. 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

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.