Watchtower is dead: doing Docker container updates right in 2026 with Diun (notify instead of blind auto-pull)
Watchtower is archived. Diun as a maintained successor – a minimal Compose example on the Docker socket that watches all containers and notifies via Telegram or email.
On 17 December 2025 the Watchtower repository was archived – no new releases, no more security patches. The very audience that relied on Watchtower for automatic container updates for years – self-hosters with Compose stacks on a VPS – now needs a maintained successor. The obvious, actively developed alternative is Diun (Docker Image Update Notifier). The decisive conceptual difference: Diun does not auto-pull, it notifies you when a new image is available. This article builds a minimal, runnable example and explains why "notify" is the more deliberate and safer choice.
What Diun does – and why not auto-pull
Diun attaches to the Docker socket, reads out the running containers, and checks on a schedule against the respective registry whether a newer digest exists for an image tag. When it finds a change, it sends a notification – via Telegram, email, and around twenty other channels. That is all. It pulls no image and restarts no container.
That sounds like less convenience than Watchtower's "set it up once, never touch it again". But that is exactly the point. A blind auto-pull on latest means: after the next restart the container runs a version you have never tested. For a stateless web app that is usually harmless. For a Postgres database it is a risk: a jump from postgres:16 to postgres:17 requires a migration of the data directory – an automatic pull that silently swaps the major image can leave the database unable to open on restart. You want to make such changes in a controlled way, with a backup and a maintenance window. Diun puts precisely that decision back in your hands: you learn that an update exists, but you decide yourself whether and when.
The minimal setup
A single compose.yaml is enough. The container starts with the argument serve, gets the Docker socket mounted read-only, and persists its state under /data:
services:
diun:
image: crazymax/diun:4.33.0
container_name: diun
command: serve
volumes:
- "./data:/data"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
environment:
- "TZ=Europe/Berlin"
- "LOG_LEVEL=info"
- "DIUN_WATCH_SCHEDULE=0 */6 * * *"
- "DIUN_WATCH_JITTER=30s"
- "DIUN_PROVIDERS_DOCKER=true"
- "DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=true"
# Notification via Telegram:
- "DIUN_NOTIF_TELEGRAM_TOKEN=<YOUR_BOT_TOKEN>"
- "DIUN_NOTIF_TELEGRAM_CHATIDS=<YOUR_CHAT_ID>"
restart: unless-stopped
Start it and watch the logs:
docker compose up -d
docker compose logs -f diun
On the first run, Diun captures all running containers and stores an initial digest for each image in ./data. From then on it checks according to DIUN_WATCH_SCHEDULE and reports only genuine changes.
The four building blocks
Docker provider. DIUN_PROVIDERS_DOCKER=true enables the Docker integration; for that Diun needs the socket mount. DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT=true watches all running containers automatically, without you having to set a label on each container. If you prefer to work selectively instead, leave this switch off and mark individual containers with the label diun.enable=true.
Schedule. DIUN_WATCH_SCHEDULE is a normal cron expression; 0 */6 * * * checks every six hours. That is enough for most cases and is gentle on registry rate limits. DIUN_WATCH_JITTER spreads many parallel checks slightly in time so that not all requests fire at once.
State under /data. This is where Diun remembers the digests it last saw. Without a persistent volume it would report everything as "new" after each restart – hence the bind mount ./data:/data.
Notifier. The DIUN_NOTIF_TELEGRAM_* variables wire up the output channel. The token comes from BotFather, the chat ID from your bot. You specify multiple recipients comma-separated.
Prefer email over Telegram?
The channel is pure configuration. For SMTP, swap the two Telegram lines for the mail notifier:
- "DIUN_NOTIF_MAIL_HOST=smtp.example.com"
- "DIUN_NOTIF_MAIL_PORT=587"
- "DIUN_NOTIF_MAIL_SSL=false"
- "DIUN_NOTIF_MAIL_USERNAME=<YOUR_SMTP_USER>"
- "DIUN_NOTIF_MAIL_PASSWORD=<YOUR_SMTP_PASSWORD>"
- "DIUN_NOTIF_MAIL_FROM=diun@example.com"
- "DIUN_NOTIF_MAIL_TO=admin@example.com"
The flow at a glance:
+----------------+ reads +---------------------+
| Diun | -------------> | docker.sock (ro) |
| command:serve | containers | running containers |
+-------+--------+ +---------------------+
| Schedule 0 */6 * * *
v
+----------------+ new digest? +------------------+
| Registry | <---------------- | Compare with |
| (Digest) | ----------------> | /data (State) |
+----------------+ yes +--------+---------+
| Notify only
v
+---------------------------+
| Telegram / email |
| -> you decide the pull |
+---------------------------+
Three common pitfalls
1. latest tells you nothing useful. Diun reports a digest change correctly, but latest does not say which version is behind it. So pin both Diun itself (crazymax/diun:4.33.0 instead of latest) and your watched containers to concrete tags. Then every notification is a deliberate, traceable version jump rather than a black box.
2. The socket is a security factor. Anyone with access to /var/run/docker.sock can do practically anything on the host. Mount it read-only (:ro) – Diun only needs read access – and treat the Diun container as carefully as any other with socket access. A socket proxy is the next step if you want to tighten this further.
3. A missing /data volume produces notification spam. Without persistent state, Diun starts from scratch on every restart and notifies you about all images again. Verify that ./data really is mounted as a volume and stays writable.
Where to go from here
You now have a maintainable replacement for Watchtower that informs you instead of acting behind your back. Obvious next steps: control individual containers deliberately via the diun.enable label, add further channels such as Gotify or a generic webhook, and read the official docs on the Docker provider, the watch options, and installation. The current image tags are on Docker Hub.
Note: The articles on this blog are produced with the help of AI and are editorially reviewed before publication. Editorial responsibility lies with Emre Yurtbay (see the Impressum).