← All articles
silhouette of tower during sunset

Watchtower: Automatic Docker Container Updates Without the Hassle

Docker 2026-03-04 · 3 min read watchtower docker automation container updates devops self-hosted
By Selfhosted Guides Editorial TeamSelf-hosting practitioners covering open source software, home lab infrastructure, and data sovereignty.

Keeping Docker containers up to date is one of the more tedious parts of self-hosting. You have to check for new images, pull them, and restart containers — for every service you run. Watchtower automates this: it watches your running containers, checks for updated images on a schedule, and restarts containers when updates are available.

Photo by Rineshkumar Ghirao on Unsplash

Whether you want fully automatic updates or just scheduled notifications, Watchtower handles it with minimal configuration.

How Watchtower Works

Watchtower runs as a Docker container alongside your other services. On a configured schedule, it:

  1. Checks each running container's image against the registry (Docker Hub by default)
  2. Pulls any updated image layers
  3. Stops the old container
  4. Starts a new container with the updated image, preserving all the original flags, volumes, and environment variables
  5. Removes the old image (optionally)

The new container is started with the same docker run arguments as the original, so your data volumes and configurations stay intact.

Basic Setup

The simplest possible deployment — add to your docker-compose.yml:

services:
  watchtower:
    image: containrrr/watchtower:latest
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true          # remove old images
      - WATCHTOWER_SCHEDULE=0 0 4 * * *  # run daily at 4am
      - TZ=America/Los_Angeles

The Docker socket mount is required — Watchtower needs to talk to the Docker daemon to manage containers.

Run docker compose up -d watchtower and Watchtower will start checking for updates on the configured schedule.

Scheduling

Watchtower uses cron syntax for scheduling. The format is: seconds minutes hours day-of-month month day-of-week

environment:
  # Daily at 4:00 AM
  - WATCHTOWER_SCHEDULE=0 0 4 * * *
  
  # Every 6 hours
  - WATCHTOWER_SCHEDULE=0 0 */6 * * *
  
  # Weekly on Sunday at 3:00 AM
  - WATCHTOWER_SCHEDULE=0 0 3 * * 0

Alternatively, set a polling interval in seconds:

environment:
  - WATCHTOWER_POLL_INTERVAL=86400  # 24 hours in seconds

The cron approach is more predictable and easier to reason about.

Selective Updates

By default, Watchtower monitors all running containers. You can control this two ways:

Exclude specific containers (allow most, block some):

# In the container you DON'T want auto-updated:
labels:
  - "com.centurylinklabs.watchtower.enable=false"

Allowlist mode (only update explicitly labeled containers):

# Watchtower configuration:
environment:
  - WATCHTOWER_LABEL_ENABLE=true

# In containers you DO want auto-updated:
labels:
  - "com.centurylinklabs.watchtower.enable=true"

The allowlist approach is safer for production: you opt-in specific services rather than having everything update automatically.

Monitor-Only Mode

If you're not ready for automatic updates but want to know when updates are available, use monitor-only mode:

environment:
  - WATCHTOWER_MONITOR_ONLY=true
  - WATCHTOWER_NOTIFICATIONS=slack
  - WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=https://hooks.slack.com/...

In this mode, Watchtower checks for updates and sends notifications but doesn't pull or restart anything. You get the awareness without the risk.

Notifications

Watchtower can send notifications via multiple channels when updates occur:

Email:

environment:
  - WATCHTOWER_NOTIFICATIONS=email
  - [email protected]
  - [email protected]
  - WATCHTOWER_NOTIFICATION_EMAIL_SERVER=smtp.yourdomain.com
  - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587
  - [email protected]
  - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=yourpassword

Slack/Discord (via webhook):

environment:
  - WATCHTOWER_NOTIFICATIONS=slack
  - WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL=https://hooks.slack.com/services/...
  - WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER=watchtower

Gotify (if you self-host notifications):

environment:
  - WATCHTOWER_NOTIFICATIONS=gotify
  - WATCHTOWER_NOTIFICATION_GOTIFY_URL=https://gotify.yourdomain.com
  - WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN=your-token

Run Once Mode

Sometimes you want to trigger an update check manually rather than on a schedule:

docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --run-once \
  --cleanup

This pulls and updates all containers immediately, then exits. Useful for applying updates before a scheduled maintenance window.

Private Registries

If your containers come from a private registry (your own Docker Hub repo, GitHub Container Registry, etc.):

environment:
  - REPO_USER=your-username
  - REPO_PASS=your-password-or-token

For multiple registries, use Docker credentials stored in ~/.docker/config.json:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock
  - /root/.docker/config.json:/config.json
environment:
  - DOCKER_CONFIG=/config.json

When to Use Automatic Updates

Good for automatic updates:

Use monitor-only or manual instead:

A sensible default: use enable=true labels on your reverse proxy, monitoring tools, and utility containers. Leave databases and stateful services set to manual updates.

Complete Example

services:
  watchtower:
    image: containrrr/watchtower:latest
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_SCHEDULE=0 0 3 * * *     # 3 AM daily
      - WATCHTOWER_LABEL_ENABLE=true         # allowlist mode
      - WATCHTOWER_NOTIFICATIONS=email
      - [email protected]
      - [email protected]
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER=mail.yourdomain.com
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT=587
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER=watchtower@yourdomain.com
      - WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD=yourpassword
      - TZ=America/Los_Angeles

  jellyfin:
    image: jellyfin/jellyfin:latest
    labels:
      - "com.centurylinklabs.watchtower.enable=true"  # auto-update this one
    # ... rest of config

  postgres:
    image: postgres:16                                 # NOT labeled — manual updates
    # ... rest of config
Get free weekly tips in your inbox. Subscribe to Self-Hosted Weekly