Diun watches container images in their registries and notifies you when a tag or digest changes. It is the notify-only alternative to Watchtower: it never pulls, never restarts and never touches a running container.
Our verdict on Diun#
Notify-only is the correct default for anything holding state, and Diun is the clean implementation of it. It reads the Docker socket, resolves each container's image against its registry on a schedule, and when a digest or tag changes it fires a notification through one of its backends: Gotify, ntfy, mail, Telegram, Discord, Slack, Matrix, Teams, Apprise, Elasticsearch, a generic webhook and more. It never pulls and never restarts, so there is no path from a bad upstream release to your data at 3am. You keep the one step automation cannot do for you, which is reading the release notes before you upgrade. The tradeoff is honest: you now run docker compose pull && docker compose up -d yourself, and if you ignore notifications for six months you end up worse off than someone who automated. watchByDefault is off, so containers opt in with diun.enable=true. v4.33.0 in May 2026 added a containerd provider and Prometheus metrics.
Who Diun is for#
Choose it if
- Anyone running PostgreSQL, Nextcloud, Immich or Vaultwarden, where an upgrade needs a human and a fresh backup first.
- Operators who already run a notification server and just want one more sender pointed at it.
- Mixed estates: Docker, Swarm, Kubernetes, Nomad, containerd and plain static image lists are all supported providers.
- People who build their own images and want to know when the base image underneath them moved.
Look elsewhere if
- You genuinely want unattended updates and everything you run is stateless. Diun deliberately will not do that, and bolting a script onto it defeats the design.
- Nobody in the household reads notifications. Notify-only with no reader is strictly worse than nothing, because it feels like coverage.
- You want a dashboard. There is no web interface, only notifications and an optional Prometheus metrics endpoint.
What running it actually looks like#
One container with /var/run/docker.sock mounted and a volume for /data, which holds the bbolt database. Set TZ, set DIUN_WATCH_SCHEDULE to a cron expression (the documented example is 0 */6 * * *), and pick a notifier. DIUN_PROVIDERS_DOCKER=true enables the Docker provider, and watchByDefault is false, so every container you care about needs the label diun.enable=true, including Diun itself if you want to know about its own updates. Add diun.watch_repo=true only where you actually want every tag enumerated, because that multiplies registry calls. runOnStartup is true and firstCheckNotif is false by default, which is what stops your first run from firing one alert per container. Upgrades are a tag bump; the Bolt schema carries forward.
Resource profile#
- Memory
- 25 MB idle, around 60 MB in ordinary use. Estimated, not documented. One Go binary plus a bbolt file; memory scales with how many images you watch and how many tags you enumerate when `watch_repo` is on.
- CPU and acceleration
- Nothing at all between scheduled runs. Each run is a burst of registry manifest lookups across `DIUN_WATCH_WORKERS` goroutines, so the constraint is network round trips and registry rate limits, not CPU.
- Storage growth
- One bbolt file holding the last-seen manifest per watched image. Kilobytes to a few megabytes, and it never meaningfully grows.
- Operational load
- 1 of 5, Set and forget. One container, one Bolt file, a cron expression and a notifier. It has no write path to anything it watches, so it cannot break your services. The worst failure mode is a missed notification, which is recoverable by looking.
Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.
Data and backup surface#
One file: /data/diun.db, a bbolt store recording the last-seen manifest for every image it watches. It is not precious. Delete it and the next run treats everything as newly seen, which with firstCheckNotif at its default of false means a quiet re-baseline rather than a notification storm. The configuration is the part worth keeping: your diun.yml or the DIUN_* environment variables, the notifier credentials, and the diun.* labels, which live in your compose files and should already be in version control. If you do back up the Bolt file, stop the container first or take a filesystem snapshot, because bbolt uses a single memory-mapped file.
Derived backup shape
Embedded key-value store. Stop the service and copy its data directory, or use the project's own export command. There is no dump utility to run against it while it is live.
Traps and surprises#
Nothing is watched until you label it
watchByDefault is false, so a freshly deployed Diun with the Docker provider enabled monitors exactly zero containers and reports nothing, which is indistinguishable from everything being up to date. Add labels: ["diun.enable=true"] to every service in your compose files, including Diun's own service. Confirm with the startup log, which lists the images it resolved, rather than waiting for a notification that will never come.
watch_repo turns one lookup into hundreds
diun.watch_repo=true enumerates every tag in the repository rather than tracking the one you run. On a repository with hundreds of tags, multiplied across your containers and run every few hours, this is how you hit Docker Hub's anonymous pull rate limit and start getting 429s that look like registry outages. Use diun.max_tags and diun.include_tags to bound it, or leave watch_repo off and track digests on your pinned tag.
Mutable tags notify on every upstream rebuild
Diun compares digests by default, so a :latest or :stable tag that upstream rebuilds nightly for a base image security patch produces a notification every night with no version change to read about. That is technically correct and practically noise, and noise is what makes people stop reading. Watch specific minor tags where the project publishes them, and reserve digest watching for images that actually change meaningfully.
The Docker socket is still full read access
Even though Diun never writes, the socket it mounts exposes environment variables, mounts and labels of every container on the host, and a compromised Diun with a writable socket would be root-equivalent. Mount it read only, and if you want to go further, put a socket proxy in front and grant only the container list and inspect endpoints. Read-only is not a security boundary on its own, but it removes the easy path.
Notifications are the whole product, so test the notifier
If the Gotify token is wrong, the ntfy topic is misspelled or the SMTP relay rejects the mail, Diun logs an error and carries on, and you experience it as an estate where nothing ever needs updating. There is no dashboard to contradict that impression. Set firstCheckNotif to true for one deliberate run, confirm the message arrives, then set it back and forget about it.
Specifications#
- Category
- Security and update hygiene
- Licence
- MIT (Permissive)
- Written in
- Go
- First release
- 2019
- Maturity
- Stable
- Datastore
- bbolt, a single file at `/data/diun.db`
- Default ports
- none
- Ships as
- docker, native binary, kubernetes
- arm64 builds
- Yes
- Identity
- Not applicable. No web interface and no accounts. It reads the Docker socket and sends notifications outward, so access control is whatever protects your notification channel.
- Replaces
- Watchtower, Renovate, Docker Hub webhooks
- Project site
- crazymax.dev/diun
- Source
- github.com/crazy-max/diun
- Documentation
- crazymax.dev/diun
Alternatives to Diun#
Everything else in security and update hygiene, closest in operational weight first.
| Tool | Ops load | Idle RAM | Licence |
|---|---|---|---|
| CrowdSec | 2, Light | 120 MB | MIT |
| Fail2ban | 2, Light | 40 MB | GPL-2.0-or-later |
| Watchtower | 2, Light | 15 MB | Apache-2.0 |
| Wazuh | 5, Project-grade | 6144 MB | GPL-2.0 |
Where Diun comes up elsewhere#
Head to head
Sources#
Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.