Skip to content

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.

install
$cargo install freshdock
Migrate in 5 minutes

What 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 freshdockRustWatchtowerarchivedfedor forkGoDiunGoWhat's Up DockerNode
Maintained in 2026YesNoarchived Dec 2025YesYesYes
Works with Docker Engine 29+YesNoAPI 1.25YesYesYes
Auto-applies updatesYesYesYesNoby designYes
Health-gated rollbackYesNoNoNot applicableno updatesNo
Per-container update policyYeslabels + modesYesYesYesYes
Notify-only modeYeswatchYesmonitor-onlyYesYesits whole jobYes
Lifecycle hooks (pre/post-update)Yessince v1.4YesYesNot applicableno updatesNo
Reads Watchtower labelsYessince v1.4Not applicablenativeNot applicablenativeNoNo
Authenticated registriesYes5YesYesYesYes
Web dashboardNoNoNoNoYesits differentiator
Footprint≤10 MB binaryGo binaryGo binaryGo binaryNode + UI
LicenseApache-2.0Apache-2.0Apache-2.0MITMIT

“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 label and flag translations to freshdock
WatchtowerfreshdockNotes
com.centurylinklabs.watchtower.enable=truefreshdock.enable=trueOpt in. Read directly since v1.4, no relabel needed.
watchtower.monitor-only=truefreshdock.mode=watchDetect + notify, never restart. Read directly since v1.4.
watchtower.lifecycle.pre-update / post-updatefreshdock.lifecycle.*Read directly; Watchtower timeouts (minutes) are converted.
WATCHTOWER_SCHEDULE (global cron)freshdock.mode + freshdock.scheduleScheduling is per-container.
watchtower.enable=false (opt-out fleet)FRESHDOCK_WATCH_ALL=trueOptional 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-onCompose depends_onThe 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

docker-compose.yml
# 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

docker-compose.yml
# 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-stopped

Runnable 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 Docker

You 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/watchtower

Ready to retire Watchtower?

Install freshdock, run freshdock check read-only first, then graduate containers to an updating mode when you trust it.