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.
| Watchtower | freshdock |
|---|---|
com.centurylinklabs.watchtower.enable=true | freshdock.enable=true |
watchtower.monitor-only=true | freshdock.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.