Skip to content

Releases: heyvaldemar/diun-docker-compose

v1.0.5

Choose a tag to compare

@heyvaldemar heyvaldemar released this 23 Sep 19:12

Fixed

  • CI had never run the restore script. The test restored with its own
    copy of the commands. The script read DATA_PATH and DATA_BACKUPS_PATH
    from the shell that ran it rather than from .env or the stack, so a path
    set in .env was not the one it listed or cleared, and it cleared with
    rm -rf dir/*, which leaves every dotfile of the newer state in place. It
    now takes every path and name from the running backups container, accepts
    the backup file name as an argument, starts the application again whatever
    happens, and CI runs it: a file written before a backup and deleted after it
    must be back once that backup is restored.

Changed

  • The freshness check has its own workflow, Pin Freshness. It ran inside Deployment Verification, whose badge is the one at the top of this README. Across the fleet, nine red runs in ten were a pin one version behind - which the fleet's triage moves within the day - and a reader cannot tell that from a stack that does not boot. The badge now says whether the stack boots. The job itself is unchanged.

v1.0.4

Choose a tag to compare

@heyvaldemar heyvaldemar released this 19 Sep 16:12

Security

  • alpine:3.22 was rebuilt upstream; the pin moved from sha256:365499d9dccb… to sha256:5291449c3df7…. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

Upgrading

git pull (or ./update.sh), then docker compose up -d. Containers on a refreshed image are recreated; data volumes and .env are untouched. This release was cut by fleet triage after the deploy job booted the stack on the refreshed images.

Full history in CHANGELOG.md.

v1.0.3

Choose a tag to compare

@heyvaldemar heyvaldemar released this 18 Sep 14:52

Security

  • alpine:3.22 was rebuilt upstream; the pin moved from sha256:14358309a308… to sha256:365499d9dccb…. Same version, same tag, a rebuilt base image — the usual shape of a security fix in a base layer.

Upgrading

git pull (or ./update.sh), then docker compose up -d. Containers on a refreshed image are recreated; data volumes and .env are untouched. This release was cut by fleet triage after the deploy job booted the stack on the refreshed images.

Full history in CHANGELOG.md.

v1.0.2

Choose a tag to compare

@heyvaldemar heyvaldemar released this 13 Sep 15:44

The delivery test, properly this time.

v1.0.1 named a cause and was wrong, so the test went on failing. Two real faults, both found by capturing what actually crossed the wire instead of reasoning about it.

Diun was posting before the sink could accept

depends_on: service_started means the container was launched, not that anything is listening, and Diun's first scan fires within a second of its own start. Every notification in that burst came back "connection refused", and because a first check happens once there was nothing to retry: the test then waited six minutes for a message that would never be sent again.

The sink has a healthcheck now and Diun waits for service_healthy. The check reads the listening socket with netstat rather than connecting to it, because connecting to a listener that serves one request at a time consumes the accept the real notification needs.

The request reached the sink whole and never reached the log

Teeing nc's output to a file showed all 669 bytes of Diun's POST, headers and body. Running the old tr -d '\r' | sed 's/^/SINK /' over that same file printed every line. Live, with nc still holding the pipe open, only the request line ever appeared, so the step hunted for a Content-Type that had arrived and been swallowed between two processes.

A shell read loop writes each line as it reads it, and the whole request reaches the log.

The -w removal from v1.0.1 stays. A listen timeout counts from the moment nc starts listening rather than from the moment a connection arrives, so it can cut an accepted request part way through. It was a real hazard. It was not this one.

Upgrading: nothing in the deployed stack changed; this is the test harness only.

v1.0.1

Choose a tag to compare

@heyvaldemar heyvaldemar released this 13 Sep 15:46

A first attempt at the delivery test's flakiness, superseded within a day by v1.0.2.

It removed -w 5 from the webhook sink's listener, on the reasoning that a listen timeout counts from the moment nc starts listening rather than from the moment a connection arrives, so it can cut an accepted request part way through. That reasoning holds and the change stays.

It was not, however, the cause of the failing test. v1.0.2 found the real ones by capturing the request to a file instead of reasoning about it: Diun was posting before the sink could accept, and the accepted request was reaching the sink whole while only its first line reached the log.

Read the v1.0.2 notes instead. Nothing in the deployed stack changed in either release; both touch the test harness only.

v1.0.0

Choose a tag to compare

@heyvaldemar heyvaldemar released this 11 Sep 01:03

First release. Diun with a read-only Docker socket proxy, built to the fleet standard.

Diun watches the image tags of everything you are running and tells you when a newer one is published. It never pulls, never recreates, never restarts. That restraint is the point: a stateful service is not something to auto-upgrade, and a major database bump wants a migration rather than a surprise.

No Traefik and no certificate here — Diun has no web interface.

A test that notifications actually leave the building

This is the test nobody writes, and for a notifier it is the one that matters most. A tool that discovers updates and cannot deliver them is, from the outside, indistinguishable from one that found nothing: both are silent, and silence is what you expect most days. The first time you learn the difference is the day it mattered.

tests/webhook-sink.yml is a receiver, and CI asserts a JSON notification arrives in it on every run.

Two defaults that are deliberately not upstream's

Watch by default. Upstream watches only containers labelled diun.enable=true. That means a new stack is unwatched until somebody remembers to label it — and nobody remembers, so coverage sits at zero for months while the tool reports itself perfectly healthy. Watching by default means coverage starts the moment a container does; exclude a noisy one with diun.enable=false.

First-check notification off. With watch-by-default, the first scan discovers everything you run at once, and the alternative empties all of it into your chat in one go.

The four labels an exactly-pinned image needs

Diun watches the digest that the running tag resolves to. For a floating tag — postgres:17, alpine:3 — that is exactly right. For an exact pin, digest-watching will never report a new version, because 10.11.12 is not a re-push of 10.11.11. It is a different tag, and nothing is watching for it. The pin keeps doing exactly what you asked; you simply stop hearing about releases.

The README carries the four labels with the reasoning rather than as a recipe, including why max_tags must be 1 (a larger window reports versions older than the one you are running, each reading as an invitation to downgrade) and why watch_repo on a rolling tag is wrong (it adds a card per tag in the window — twenty cards in a minute, on one host).

Diun never gets the Docker socket

Diun's job is to read what you are running, and it never acts on anything, so it has no business holding a socket that could — and :ro on a socket mount would not stop it, because the API is root-equivalent whichever way the file is mounted. A proxy holds it and forwards CONTAINERS and IMAGES only, with POST denied. Both, and the second is the one people leave out: without IMAGES the listing succeeds, every image inspect gets a 403, and the log says No image found — a stack that is healthy, running and watching nothing.

Three things the build found

IMAGES missing from the allow-list fails quietly. Found by pointing the first draft at a proxy and reading what Diun made of it rather than what the proxy answered. CI asserts the discovery count for exactly that reason.

Diun ignores DOCKER_HOST. Its own setting is DIUN_PROVIDERS_DOCKER_ENDPOINT, and a proxy configured the other way is silently bypassed in favour of a socket that is not mounted.

A shell pipeline cannot begin a line with |. The sink's command, written across a folded YAML scalar with the pipes leading each continuation, died instantly, restarted forever, and dropped out of Docker's DNS as it went — so Diun reported "no such host", which reads like a network problem and was a shell one.

Also included

Three images pinned by tag@sha256:<digest> — with a pleasing circularity, since this stack watches every pin in every other one and its own are watched by the same daily freshness check. A backup loop that reads its own archive back before naming it a backup, an end-to-end suite requiring diun.db in the archive by name, a restore script, update.sh, cap_drop: ALL, resource limits, and OpenSSF Scorecard.

Full documentation: README · CHANGELOG