Skip to content
All posts
migrationwatchtower

Migrating from Watchtower to freshdock in 5 minutes

A practical, copy-paste migration from archived Watchtower to freshdock: translate labels, swap the service in docker-compose, and verify read-only first.

Watchtower is archived and breaks on Docker Engine 29+. Moving to freshdock is mostly a relabel plus a service swap. Here's the whole thing, start to finish.

1. Install freshdock

Pick whichever fits. The result is the same single binary:

cargo install freshdock
# or pull the multi-arch image
docker pull ghcr.io/turbootzz/freshdock:latest

Full options on the install page.

2. Translate your labels

The concepts map closely; the spelling changes. The big one to internalise: freshdock is opt-in, so you rarely need to disable anything. Unlabelled containers are simply ignored.

Watchtowerfreshdock
com.centurylinklabs.watchtower.enable=truefreshdock.enable=true
watchtower.monitor-only=truefreshdock.mode=watch
WATCHTOWER_SCHEDULE (one global cron)per-container freshdock.mode + freshdock.schedule
watchtower.enable=false (with global watch)just omit the labels

Two Watchtower features have no freshdock equivalent today: no-pull (freshdock always pulls before recreate) and depends-on dependency ordering. If you rely on those, check the comparison page before switching.

3. Swap the service in docker-compose

Replace the Watchtower service and relabel your apps. Before:

services:
  app:
    image: ghcr.io/example/app:latest
    labels:
      - "com.centurylinklabs.watchtower.enable=true"

  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_SCHEDULE=0 0 4 * * *

After:

services:
  app:
    image: ghcr.io/example/app:latest
    labels:
      - "freshdock.enable=true"
      - "freshdock.mode=nightly"   # 04:00 daily

  freshdock:
    image: ghcr.io/turbootzz/freshdock:latest
    command: ["run"]
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped

A read-only socket (:ro) is enough while everything is on watch; give freshdock a writable socket once a container is on an updating mode like nightly.

4. Verify read-only, then commit

Before you let it change anything, run the read-only check. It lists your opted-in containers and what has updates, and never pulls, stops, or recreates:

freshdock check

Happy with the table? You're done. The daemon (freshdock run) will now health-gate every update and roll back any that fail to come up.

Notifications and registries

If you used Watchtower's shoutrrr notifications, freshdock has webhook, Discord, Telegram, and SMTP backends, declared in a small freshdock.toml, with secrets supplied via environment variables. Private registry credentials (Docker Hub, GHCR, Quay, lscr) come from FRESHDOCK_REGISTRY_* env vars, no file required.

The complete label, flag, env, notification, and registry translation table (more than fits here) is the migration guide on the docs site. That's the authoritative reference; this post is just the five-minute path.