Understanding Watchtower Configuration
Watchtower Docker configuration is entirely environment-variable driven — there is no configuration file to edit, no YAML to write, and no CLI flags required (though CLI flags are also supported for most settings). This design philosophy makes Watchtower particularly well-suited for Docker Compose and Kubernetes deployments where environment variables are the standard configuration mechanism.
Environment variables can be passed three ways:
# 1. Inline with docker run
docker run -d \
-e WATCHTOWER_CLEANUP=true \
-e WATCHTOWER_POLL_INTERVAL=3600 \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
# 2. Docker Compose environment block
services:
watchtower:
image: containrrr/watchtower
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_POLL_INTERVAL=3600
# 3. Docker Compose env_file
services:
watchtower:
image: containrrr/watchtower
env_file:
- watchtower.env
The env_file approach keeps secrets (registry credentials, webhook URLs) out of your Compose file and version control.
Update Behavior Variables
These are the core Watchtower Docker environment variables that control how and when updates happen.
| Variable | Default | Description |
|---|---|---|
WATCHTOWER_POLL_INTERVAL | 86400 | Seconds between update checks (default: 24 hours). Set to 3600 for hourly checks. |
WATCHTOWER_SCHEDULE | — | 6-field cron expression (seconds first). Overrides POLL_INTERVAL when set. Example: 0 0 4 * * * = 4 AM daily. |
WATCHTOWER_CLEANUP | false | Set to true to delete old images after successful update. Strongly recommended for production. |
WATCHTOWER_REMOVE_VOLUMES | false | Remove attached volumes when replacing containers. Use with caution — data loss risk. |
WATCHTOWER_INCLUDE_RESTARTING | false | Include containers with restarting state in update checks. |
WATCHTOWER_INCLUDE_STOPPED | false | Also check stopped containers for updates (won't restart them unless new image found). |
WATCHTOWER_REVIVE_STOPPED | false | Restart stopped containers after updating them. Requires WATCHTOWER_INCLUDE_STOPPED=true. |
WATCHTOWER_ROLLING_RESTART | false | Update containers one at a time rather than all simultaneously. Recommended for Swarm. |
WATCHTOWER_TIMEOUT | 10s | Timeout for stopping a container before forced kill. Accepts duration strings like 30s, 2m. |
Scope Control — Which Containers to Watch
These Watchtower Docker config options control which containers Watchtower monitors and updates.
| Variable | Default | Description |
|---|---|---|
WATCHTOWER_LABEL_ENABLE | false | When true, only update containers that have com.centurylinklabs.watchtower.enable=true label. Opt-in mode. |
WATCHTOWER_MONITOR_ONLY | false | Detect updates and send notifications, but never pull images or restart containers. Read-only mode. |
WATCHTOWER_SCOPE | — | Instance scope name. Allows multiple Watchtower instances to manage different container subsets on the same host. |
# Opt-in mode — only update explicitly labeled containers
environment:
- WATCHTOWER_LABEL_ENABLE=true
# Monitor-only — notify but never update
environment:
- WATCHTOWER_MONITOR_ONLY=true
- WATCHTOWER_NOTIFICATION_URL=slack://token@channel
Scheduling and Time Configuration
The Watchtower Docker schedule system supports both simple polling intervals and full cron expressions.
# Simple interval (seconds) — check every 6 hours
- WATCHTOWER_POLL_INTERVAL=21600
# Cron schedule — 6-field format (SECONDS MINUTES HOURS DAY MONTH WEEKDAY)
- WATCHTOWER_SCHEDULE=0 0 4 * * * # 4:00 AM every day
- WATCHTOWER_SCHEDULE=0 0 */6 * * * # Every 6 hours
- WATCHTOWER_SCHEDULE=0 30 2 * * 0 # 2:30 AM every Sunday
Timezone Configuration
To ensure your Watchtower Docker timezone is correct for scheduled runs and log timestamps:
services:
watchtower:
image: containrrr/watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /etc/localtime:/etc/localtime:ro # Mount host timezone
environment:
- TZ=America/New_York # Or: Europe/London, Asia/Karachi, etc.
- WATCHTOWER_SCHEDULE=0 0 3 * * * # Will fire at 3 AM in the configured TZ
Notification Variables
Full notification configuration reference — these Watchtower Docker variables control how and where update alerts are sent.
| Variable | Description |
|---|---|
WATCHTOWER_NOTIFICATIONS | Comma-separated list: slack, email, msteams, gotify. Legacy method. |
WATCHTOWER_NOTIFICATION_URL | Shoutrrr URL string (preferred). Supports all services. Multiple URLs space-separated. |
WATCHTOWER_NOTIFICATION_REPORT | Set to true to batch notifications per run (one message per update cycle). |
WATCHTOWER_NOTIFICATION_TITLE_TAG | Custom tag appended to notification titles for multi-host environments. |
WATCHTOWER_NOTIFICATION_SKIP_TITLE | Set to true to omit the title prefix from notifications. |
WATCHTOWER_NOTIFICATION_EMAIL_FROM | Sender email address for SMTP notifications. |
WATCHTOWER_NOTIFICATION_EMAIL_TO | Recipient email address for SMTP notifications. |
WATCHTOWER_NOTIFICATION_EMAIL_SERVER | SMTP server hostname. |
WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT | SMTP port (587 for TLS, 465 for SSL, 25 for plaintext). |
WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL | Slack incoming webhook URL. |
WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER | Bot name displayed in Slack messages. Default: watchtower. |
# Modern Shoutrrr method — single variable covers all services
environment:
- WATCHTOWER_NOTIFICATION_URL=slack://T00/B00/xxx
# Telegram: telegram://botToken@telegram?channels=@channelName
# Discord: discord://webhookToken@webhookID
# Ntfy: ntfy://topic@ntfy.sh
# Gotify: gotify://hostname/token
Private Registry Authentication
For Watchtower Docker private registry access, Watchtower reads Docker Hub credentials from the standard Docker config file or environment variables.
# Method 1: Mount Docker config (recommended)
volumes:
- /root/.docker/config.json:/config.json:ro
# OR
- $HOME/.docker/config.json:/config.json:ro
# Method 2: Environment variables for Docker Hub
environment:
- REPO_USER=myusername
- REPO_PASS=mypassword
# Method 3: Per-registry credentials (multi-registry)
environment:
- REPO_USER__registry.example.com=myuser
- REPO_PASS__registry.example.com=mypassword
For private registries like AWS ECR, GitHub Container Registry (ghcr.io), or GitLab Registry, use the mounted config.json approach — run docker login registry.example.com on the host first, which writes credentials to ~/.docker/config.json.
Docker Connection Variables
Control how Watchtower connects to the Docker daemon — useful for Watchtower Docker socket proxy setups or remote Docker hosts.
| Variable | Default | Description |
|---|---|---|
DOCKER_HOST | unix:///var/run/docker.sock | Docker daemon socket or TCP address. Set to tcp://host:2375 for remote. |
DOCKER_TLS_VERIFY | false | Enable TLS verification for remote Docker connections. |
DOCKER_CERT_PATH | — | Path to TLS certificates directory for secure remote connections. |
DOCKER_API_VERSION | auto-detect | Force a specific Docker API version. Use when auto-detection fails. |
# Socket proxy setup (more secure than raw socket)
environment:
- DOCKER_HOST=tcp://socket-proxy:2375 # Tecnativa Docker Socket Proxy
# Remote Docker host (with TLS)
environment:
- DOCKER_HOST=tcp://remote-host:2376
- DOCKER_TLS_VERIFY=1
- DOCKER_CERT_PATH=/certs
Rate Limiting and Pull Control
Docker Hub enforces rate limits on image pulls — anonymous users get 100 pulls/6h, authenticated users get 200 pulls/6h, and Docker Pro/Team gets more. With many containers, Watchtower can hit these limits.
environment:
# Reduce check frequency to stay under rate limits
- WATCHTOWER_POLL_INTERVAL=86400 # Check once per day
# Authenticate to get higher rate limits
- REPO_USER=dockerhubuser
- REPO_PASS=dockerhubpassword
# Per-container: disable Watchtower for non-critical containers
# (add this label to individual containers)
# com.centurylinklabs.watchtower.enable=false
WATCHTOWER_POLL_INTERVAL default is 86400 (24 hours) — this is intentionally conservative to respect Docker Hub rate limits. Only decrease the interval if you have authenticated access or your own registry.HTTP API Variables
Watchtower includes an optional HTTP API for triggering updates programmatically and querying status — useful for CI/CD pipelines.
environment:
- WATCHTOWER_HTTP_API_UPDATE=true # Enable POST /v1/update endpoint
- WATCHTOWER_HTTP_API_METRICS=true # Enable GET /v1/metrics (Prometheus)
- WATCHTOWER_HTTP_API_TOKEN=mySecretToken # Bearer token for auth
# Expose the API port
ports:
- "8080:8080"
# Trigger an update via API:
# curl -H "Authorization: Bearer mySecretToken" http://localhost:8080/v1/update
Logging and Debug Variables
| Variable | Default | Description |
|---|---|---|
WATCHTOWER_DEBUG | false | Enable verbose debug logging. Equivalent to --log-level debug. |
WATCHTOWER_TRACE | false | Enable trace-level logging (very verbose — includes API responses). |
NO_COLOR | false | Disable ANSI color codes in log output (useful for log aggregation). |
# Full debug configuration for troubleshooting
environment:
- WATCHTOWER_DEBUG=true
# Then check logs:
# docker logs watchtower --follow
Complete Production Example
A production-ready Docker Compose configuration combining the most important Watchtower Docker environment variables:
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /etc/localtime:/etc/localtime:ro
- /root/.docker/config.json:/config.json:ro
environment:
# Scheduling
- WATCHTOWER_SCHEDULE=0 0 4 * * * # 4 AM daily
- TZ=America/New_York
# Update behavior
- WATCHTOWER_CLEANUP=true # Remove old images
- WATCHTOWER_ROLLING_RESTART=true # One at a time
- WATCHTOWER_TIMEOUT=30s # 30s stop timeout
# Scope
- WATCHTOWER_LABEL_ENABLE=false # Update all containers
# Notifications (Slack via Shoutrrr)
- WATCHTOWER_NOTIFICATION_URL=slack://T00/B00/secret
# HTTP API
- WATCHTOWER_HTTP_API_UPDATE=true
- WATCHTOWER_HTTP_API_TOKEN=yourSecretToken
Frequently Asked Questions
WATCHTOWER_CLEANUP=true tells Watchtower to automatically remove the old image after successfully updating a container to a newer version. Without this setting, old images accumulate on disk indefinitely. Setting WATCHTOWER_CLEANUP=true is recommended for all production deployments to reclaim disk space.
Set TZ=America/New_York (or your timezone) as an environment variable, and mount /etc/localtime:/etc/localtime:ro. This ensures log timestamps and scheduled run times (via WATCHTOWER_SCHEDULE) reflect your local time zone. Use IANA timezone names like Europe/London, Asia/Karachi, Australia/Sydney.
WATCHTOWER_MONITOR_ONLY=true puts Watchtower into a passive mode — it detects when new image versions are available and sends notifications, but never actually pulls new images or restarts containers. This is ideal for production environments where you want visibility into available updates without automatic restarts.
Set WATCHTOWER_LABEL_ENABLE=true globally, then add the label com.centurylinklabs.watchtower.enable=true only to containers you want Watchtower to manage. All other containers will be ignored. Alternatively, use com.centurylinklabs.watchtower.enable=false on specific containers you want to exclude while updating all others by default.
WATCHTOWER_POLL_INTERVAL is a simple repeat interval in seconds — e.g., 3600 means check every hour. WATCHTOWER_SCHEDULE is a 6-field cron expression that lets you specify exact times, like "only at 4 AM on weekdays." When WATCHTOWER_SCHEDULE is set, it takes precedence over WATCHTOWER_POLL_INTERVAL. Use POLL_INTERVAL for simple recurring checks and SCHEDULE for time-specific runs.