Skip to main content

Migrating from Watchtower

Move from Watchtower to Outpost by translating your settings and labels, and know up front which Watchtower options don't carry over.

Why Migrate?

  1. Gets automatic rollback so an update that breaks a container no longer leaves it down.
  2. Keeps anonymous volume data and the new image's defaults through every recreation.
  3. Runs the updater as a non-root user that only joins the Docker socket's group.
  4. Moves to a project that is actively maintained.

Migration Steps

  1. Stop and remove the Watchtower container so the two never update the same container at once.
  2. Translate its environment variables into Outpost settings and your container labels with the tables below.
  3. If Watchtower pulled private images, add a sign-in for each registry under Private registries on Outpost's Settings page, as shown in Private Registries.
  4. Turn on Monitor only on Outpost's Settings page and check the first report.
  5. If scripts or Prometheus called Watchtower's API, create an access token for each one on the dashboard's Access tokens page.
  6. If Watchtower sent notifications, add a destination for each service on the dashboard's Notifications page, and select Send test.
  7. Turn off Monitor only once the results look right.

Environment Variables and Settings

WatchtowerOutpostNotes
WATCHTOWER_SCHEDULESchedule on the Settings pageSix-field cron with seconds carries over, but replace ? with *, since Outpost reads ? as the time it started. @every intervals need rewriting as cron.
WATCHTOWER_POLL_INTERVALSchedule on the Settings pageNo interval setting. Express it as cron, such as 0 0 */6 * * *.
WATCHTOWER_RUN_ONCECheck when Outpost startsOutpost runs at start, but keeps running afterwards.
WATCHTOWER_LABEL_ENABLEContainer selection set to Opt-in
WATCHTOWER_MONITOR_ONLYMonitor only
WATCHTOWER_CLEANUPClean up old imagesOutpost cleans up by default. Turn it off to keep old images.
WATCHTOWER_TIMEOUTStop timeoutWhole seconds, such as 30, not a duration like 30s.
WATCHTOWER_HTTP_API_TOKENAn access tokenCreate one for each client on the Access tokens page. Outpost makes the token's value, so each client needs the new one.
WATCHTOWER_HTTP_API_UPDATEThe updates:run scopePOST /v1/update is always on, and only tokens with updates:run can call it.
WATCHTOWER_HTTP_API_METRICSThe metrics:read scope/metrics is always on, and only tokens with metrics:read can read it. Metric names differ.
WATCHTOWER_HTTP_API_PERIODIC_POLLSNoneNot needed. The schedule always runs alongside the API.
WATCHTOWER_ROLLING_RESTARTNoneNot needed. Outpost always updates one container at a time.
WATCHTOWER_NOTIFICATION_URLDestinations on the Notifications pageOutpost doesn't read Shoutrrr URLs, so add one destination per URL and fill in its fields. ntfy, Telegram, Discord, Slack, Gotify, Pushover, Matrix, and email have their own service, and Apprise reaches most others.
WATCHTOWER_NOTIFICATIONSDestinations on the Notifications pageEmail, Slack, and Gotify have their own service, and Microsoft Teams works through Apprise.
WATCHTOWER_NOTIFICATIONS_LEVELThe events each destination choosesPick events, such as Update failed, instead of a log level.
WATCHTOWER_NOTIFICATION_REPORTNoneEach alert covers one event and lists its containers.
WATCHTOWER_NOTIFICATION_TEMPLATENoneThe wording of each alert is fixed.
WATCHTOWER_INCLUDE_STOPPEDNoneOutpost checks running containers only.
WATCHTOWER_REVIVE_STOPPEDNone
WATCHTOWER_INCLUDE_RESTARTINGNone
WATCHTOWER_NO_PULLNone
WATCHTOWER_NO_RESTARTNone
WATCHTOWER_SCOPENoneRun one Outpost per host and use labels instead.
WATCHTOWER_DISABLE_CONTAINERSNoneLabel those containers io.cbnventures.outpost.enable=false.
WATCHTOWER_LIFECYCLE_HOOKSNonePre-update and post-update commands are not supported.
WATCHTOWER_DEBUG, WATCHTOWER_TRACENoneOutpost logs one line per container per cycle.
REPO_USER, REPO_PASSPrivate registries on the Settings pageAdd a sign-in for each registry host, with its username and a password or registry access token.
DOCKER_HOSTDOCKER_HOSTUnix sockets and plain tcp:// only.
DOCKER_TLS_VERIFYNoneTLS connections to the Docker daemon are not supported.
DOCKER_CONFIGNoneOutpost doesn't read config.json. Add each login under Private registries on the Settings page instead.

Container names passed as arguments, such as watchtower nginx redis, have no equivalent. It is recommended to use opt-in mode with labels instead.

Labels

WatchtowerOutpostNotes
com.centurylinklabs.watchtower.enableio.cbnventures.outpost.enableOutpost accepts only lowercase true or false.
com.centurylinklabs.watchtower.monitor-onlyio.cbnventures.outpost.monitor-onlytrue makes the container monitor-only.
com.centurylinklabs.watchtower.depends-onNoneCompose depends_on is read automatically.
com.centurylinklabs.watchtower.scopeNone
com.centurylinklabs.watchtower.no-pullNone
com.centurylinklabs.watchtower.stop-signalNoneDocker uses the container's own stop signal.
com.centurylinklabs.watchtower.lifecycle.*NoneLifecycle hooks are not supported.

Outpost ignores Watchtower labels entirely. You can keep them while you migrate, but they have no effect.

API and Metrics

  • Port — The API listens on port 22601 rather than Watchtower's 8080, so update port mappings, scripts, and scrape targets.
  • Tokens — Watchtower's one shared token becomes an access token for each client, with only the scopes it needs. Clients still send it as Authorization: Bearer, so only the token's value changes.
  • Update — Watchtower's /v1/update becomes POST /v1/update, which returns 202 or 409 instead of waiting for the run to finish. A GET, which is what plain curl sends, gets 404 with the code not_found.
  • Single images — POST /v1/update ignores query parameters, so Watchtower's ?image= has no equivalent there. To check one container, send its ID to POST /api/v1/checks, as shown in HTTP API and Metrics.
  • Status — GET /v1/status is new and returns the latest report as JSON.
  • Metrics — /metrics uses outpost_* names, so dashboards and alerts built on watchtower_* metrics need updating. See HTTP API and Metrics for the list.

Behavior Differences

  • Rollback — A new container that stops or turns unhealthy is replaced by the previous one.
  • Self-updates — Like Watchtower, Outpost updates its own container by default, but last in each check and through a helper container that puts the old one back if the new one fails. To keep it on its current image, label it io.cbnventures.outpost.enable=false, and in opt-in mode, label it io.cbnventures.outpost.enable=true to let it update itself.
  • Cleanup default — Old images are removed after an update unless you turn off Clean up old images in Settings.
  • Shared network namespaces — Containers with network_mode: container:... or service:... are skipped and need updating by hand.
  • Registry logins — Outpost doesn't read Docker's config.json or run credential helpers such as credsStore. Add each login on the Settings page instead; see Private Registries.