Free weekly email: the week's new posts, videos and one tip from the books, every Sunday. Get it free →
Homelab the Right Way

Home › Posts

Watchtower is archived: pin your tags, get notified with Diun, update on purpose

The auto-updater most homelabs relied on is no longer maintained. Here is the safer habit from Containers Without the Chaos - pinned image tags, Diun notifications, and a six-step update routine with a rollback built in.

· 5 min read · dockerdocker composediunwatchtowerupdateshomelab

Watchtower is archived: pin your tags, get notified with Diun, update on purpose

For years, the default answer to "how do I keep my containers updated?" was Watchtower: run one more container and it replaces the others whenever a new image appears. That answer has expired. The Watchtower GitHub repository (containrrr/watchtower) was archived by its owner on December 17, 2025, and its README says the project is no longer maintained. Containers Without the Chaos puts it plainly: do not build a new setup on it.

That is a good moment to look at the whole update question again, because blind auto-updating was never the best fit for a homelab in the first place.

The trap: two opposite ways to get updates wrong

The book names two common traps, and they pull in opposite directions.

  • Never updating. Images that nobody touches run known vulnerabilities for years.
  • Updating blindly. An automatic updater pulls latest at 4 a.m., a breaking release arrives while you sleep, and you find out when the family does.

There is a quieter version of the second trap too: thinking you have pinned something when you have not. The tag latest is just a name. docker compose pull fetches whatever it points to today. The book calls this out as a common mistake: pinning latest and calling it pinned.

Step 1: pin tags you can reason about

An image reference can be loose or exact. The book lays out the options:

PinExampleWhat it means
Latestapp:latestWhatever the project pushed most recently
Majorapp:2Updates within version 2; usually safe
Minorapp:2.4Bug fixes only, if the project tags that way
Exactapp:2.4.1Never changes; you decide when to move
Digestapp@sha256:...Byte-for-byte the same image, forever

For anything the family relies on, pin an exact version or at least a major version, and move it on purpose. latest is fine for throwaway experiments.

The book's tip makes the exact pin painless: put the version in the stack's .env file and reference it from compose.yaml.

# .env
NOTES_VERSION=2.4.1
services:
  notes:
    image: ghcr.io/example/notes:${NOTES_VERSION}

Compose reads .env in the project folder automatically. An upgrade is now a one-line change you can see in a diff.

Step 2: get told, don't get surprised

The book compares three approaches:

ApproachHowFits
Manualdocker compose pull then up -dEverything; you choose when
AutomaticA tool replaces containers on its ownLow-risk apps only
NotifyA tool tells you; you update by handThe sweet spot

Notify is the one it recommends for a homelab. The tool it uses is Diun (Docker Image Update Notifier). Diun watches the images your containers use and tells you when a new tag or digest appears. It changes nothing; you stay in charge.

Here is the book's stack for it:

name: diun

services:
  diun:
    image: crazymax/diun:4
    command: serve
    restart: unless-stopped
    volumes:
      - ./data:/data
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      TZ: ${TZ}
      DIUN_WATCH_SCHEDULE: "0 */6 * * *"
      DIUN_PROVIDERS_DOCKER: "true"
      DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT: "true"
      DIUN_NOTIF_NTFY_ENDPOINT: https://ntfy.home.example.com
      DIUN_NOTIF_NTFY_TOPIC: diun

What the settings do:

  • The schedule checks every six hours.
  • watchByDefault watches every container. Set it to false and add the label diun.enable=true only to the containers you care about, if you want a quieter feed.
  • The notification target here is ntfy, which pushes to your phone. Diun can also notify email, Gotify, Matrix, Telegram and webhooks, among others.

Each environment variable maps to a documented Diun setting (watch.schedule, providers.docker.watchByDefault, notif.ntfy.endpoint, notif.ntfy.topic). If you prefer, a YAML file (diun.yml) works too; Diun's docs list both forms for every option.

One caution from the book's Docker install chapter applies here: anything that can talk to /var/run/docker.sock controls the host, and that includes update tools. Mount it read-only where the tool allows it, as above, and only into tools you trust.

Step 3: the update routine

When a notification arrives, update one stack at a time. The book's loop is read, pull, recreate, check, and keep the old tag for rollback:

  1. Read the release notes for the new version. Look for "breaking", "migration" and "manual steps".
  2. Take a backup or a Proxmox snapshot of the VM if the app holds data you care about.
  3. Change the tag in .env, for example NOTES_VERSION=2.5.0.
  4. Pull and recreate: docker compose pull && docker compose up -d.
  5. Check: docker compose ps shows healthy, the logs are quiet, the app works.
  6. Roll back if it doesn't: put the old tag back in .env and run docker compose up -d again. Database migrations may not go backwards; that is what step 2 is for.

Because the version lives in one line of .env, steps 3 and 6 are the same edit in opposite directions.

Databases are the exception

Major database upgrades (PostgreSQL 16 to 17, for example) usually need a dump and restore, not just a new tag. The image's documentation says what is required. Pin the database's major version and upgrade it on purpose, separately from the app.

Clean up, but not too early

Old images pile up. The book's commands:

docker image prune
docker system df

docker image prune removes dangling images. Adding -a removes every image not used by a container, which also deletes the old version you might want to roll back to. Run it only after the new version has proven itself.

Checklist

  • Nothing new depends on Watchtower
  • No image the family relies on runs on latest
  • Image versions live in each stack's .env and compose.yaml references them
  • Diun (or another notifier) reports new image versions to your phone
  • The Docker socket is mounted read-only into Diun
  • Before an update: release notes read, backup or snapshot taken
  • After an update: docker compose ps healthy, logs quiet, app works
  • Databases pinned to a major version and upgraded separately
  • docker image prune -a only after the new version has proven itself

The result is the opposite of both traps: nothing runs for years untouched, and nothing changes at 4 a.m. without you. Updates become a short, boring routine you run when you choose, which is exactly what a homelab host should be.

More articles