Watchtower alternative
freshdock: a maintained Watchtower alternative
Watchtower was archived on 17 December 2025 and no longer works with Docker Engine 29+. freshdock is an actively maintained, open-source replacement. It's a single Rust binary that adds health-gated rollback and opt-in, per-container update policies. And since v1.4 it reads your existing watchtower.* labels, so most fleets migrate without relabelling.
$cargo install freshdockWhat happened
Why Watchtower stopped being a safe default.
archived
The maintainers archived containrrr/watchtower on 17 December 2025. No more fixes, no more releases.
incompatible
It embeds an old Docker SDK (API 1.25). Docker Engine 29+ requires API ≥ 1.44, so Watchtower simply can’t talk to a current daemon.
fork caveat
Community forks exist and keep it running, but they’re stop-gaps on a Go codebase, not a rethink of the safety model.
Head to head
freshdock vs the alternatives
An honest scorecard. freshdock doesn’t win every row, and where a tool is deliberately different, it’s noted.
| Capability | freshdockRust | Watchtowerarchived | fedor forkGo | DiunGo | What's Up DockerNode |
|---|---|---|---|---|---|
| Maintained in 2026 | Yes | Noarchived Dec 2025 | Yes | Yes | Yes |
| Works with Docker Engine 29+ | Yes | NoAPI 1.25 | Yes | Yes | Yes |
| Auto-applies updates | Yes | Yes | Yes | Noby design | Yes |
| Health-gated rollback | Yes | No | No | Not applicableno updates | No |
| Per-container update policy | Yeslabels + modes | Yes | Yes | Yes | Yes |
| Notify-only mode | Yeswatch | Yesmonitor-only | Yes | Yesits whole job | Yes |
| Lifecycle hooks (pre/post-update) | Yessince v1.4 | Yes | Yes | Not applicableno updates | No |
| Reads Watchtower labels | Yessince v1.4 | Not applicablenative | Not applicablenative | No | No |
| Authenticated registries | Yes5 | Yes | Yes | Yes | Yes |
| Web dashboard | No | No | No | No | Yesits differentiator |
| Footprint | ≤10 MB binary | Go binary | Go binary | Go binary | Node + UI |
| License | Apache-2.0 | Apache-2.0 | Apache-2.0 | MIT | MIT |
“fedor fork” = nicholas-fedor/watchtower, the active community fork of the original.
Label translation
Your labels keep working. Translating is optional.
Since v1.4 freshdock reads the com.centurylinklabs.watchtower.* labels directly, and a freshdock.* label always wins when both are present. Translate at your own pace, or not at all.
| Watchtower | freshdock | Notes |
|---|---|---|
| com.centurylinklabs.watchtower.enable=true | freshdock.enable=true | Opt in. Read directly since v1.4, no relabel needed. |
| watchtower.monitor-only=true | freshdock.mode=watch | Detect + notify, never restart. Read directly since v1.4. |
| watchtower.lifecycle.pre-update / post-update | freshdock.lifecycle.* | Read directly; Watchtower timeouts (minutes) are converted. |
| WATCHTOWER_SCHEDULE (global cron) | freshdock.mode + freshdock.schedule | Scheduling is per-container. |
| watchtower.enable=false (opt-out fleet) | FRESHDOCK_WATCH_ALL=true | Optional opt-out mode. Excluded with freshdock.mode=off, or the watchtower label you already have. |
| watchtower.no-pull=true | (no equivalent) | freshdock always pulls before recreate. |
| watchtower.depends-on | Compose depends_on | The label is warned about and ignored. Inside a Compose project freshdock orders updates by Compose’s own depends_on instead, no extra labels. |
The full label and flag/env translation table (including notifications and registry auth) lives in the migration guide ↗.
Replace it in compose
Swap the service. Relabelling is up to you.
Existing watchtower.* labels keep working; the freshdock.* labels shown here are the native spelling with the finer-grained knobs (modes, cron, cleanup, notify).
- before
# Watchtower (archived)
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
# freshdock
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-stoppedRunnable example stacks live in examples/compose ↗.
Honesty block
When freshdock is the wrong tool.
It’s scoped on purpose. If one of these is you, another tool fits better today.
You run Kubernetes or Swarm
Those platforms have their own rollout and image-update mechanisms. freshdock targets plain Docker / Podman and deliberately stays out of their way.
You need a web dashboard today
freshdock is a CLI and a daemon. There is no web UI right now, on purpose: it stays small and out of the way. If enough people ask, one can be built. Until then, if a browser dashboard with approve buttons is the point, What’s Up Docker has one.
What's Up DockerYou depend on update-without-pull
freshdock always pulls before it recreates, so Watchtower’s no-pull has no equivalent. Everything else maps: your watchtower.* labels are read directly, FRESHDOCK_WATCH_ALL=true restores the opt-out model, and update ordering comes from Compose’s own depends_on instead of a label. If no-pull is a hard requirement, the active community fork is the lift-and-shift option.
nicholas-fedor/watchtowerReady to retire Watchtower?
Install freshdock, run freshdock check read-only first, then graduate containers to an updating mode when you trust it.