Container image release notes: tags, digests, and CVE rebuilds
A container image is the most opaque artifact most teams ship. Your users never see
the code, never run the build, and usually never read the Dockerfile β they run
docker pull and deploy whatever comes back, frequently into
production, frequently via automation that nobody watches. That makes image release
notes less like app-store copy and more like
firmware notes: you are shipping updates
to systems you don’t control, to people who will only find out what changed when
something behaves differently at 3am.
Container publishing also has two properties that make the usual changelog habits actively misleading: tags are mutable pointers, and the artifact changes even when your code doesn’t. Both need to show up in how you write and publish notes β and neither is covered by pointing at your git history.
Tags are pointers, not versions
A git tag names a commit forever. A container tag is a label you can silently move to a different image any time you push. That difference drives everything:
- Rolling tags (
latest,1,1.4) are a subscription: pulling them tomorrow gets a different artifact than today. Useful β but only if there’s a changelog explaining what the subscription delivered. - Immutable version tags (
1.4.2) are a promise you have to keep by policy, because the registry won’t enforce it for you. Re-pushing1.4.2with different contents is a silent re-release β the same trust failure the hotfix guide warns about, except in containers it’s endemic because nothing stops you. - Digests (
sha256:…) are the only immutable name an image has. Cautious consumers pin them; automated updaters bump them. Your release notes are what tells a digest-pinned consumer whether moving to the new digest is worth it.
Write your tag policy down once, on the same page as the changelog: which tags
roll, which are immutable, whether you ever re-push, and what
latest actually tracks (stable? edge? the last thing CI
built?). Every fifth issue on a popular image’s tracker is someone discovering
the answer the hard way. If you do have to replace a version tag β a botched build,
a broken manifest β treat it like a hotfix: same-version re-push plus a dated entry
saying the digest changed and why, never a quiet swap.
The release where your code didn’t change
Base images ship CVE fixes constantly, so responsible publishers rebuild on a schedule: same application code, new base layers, new digest. This is a release. It changes what people run in production, it changes what their vulnerability scanner says, and it deserves an entry β even a one-liner:
2026-07-30 β Rebuild on alpine 3.20.3 No application changes. Picks up base-image fixes for CVE-2026-31742 and CVE-2026-30198 (both in libcrypto; neither reachable from the default configuration, patched anyway). All 1.4.x tags re-pointed; digests updated below.
Two audiences need this. Scanner-driven users see Trivy or Grype flag your image and want to know whether a fixed build exists before they file the issue β the entry is the deflection. Change-averse users see a new digest appear with no release in your git repo and want to know why; “rebuild, no app changes” is exactly the reassurance that lets them wave it through. Vague variants (“security rebuild” with no CVE list) fail both audiences the same way “security improvements” fails everywhere else β the security advisory guide covers why precision about impact beats vagueness even when you keep the mechanism quiet.
What actually breaks image consumers
The image’s interface is not its API β it’s everything a deployment manifest, healthcheck, or debug session touches. These are the changes that must be flagged as breaking (the breaking-changes playbook applies in full), and most of them never look breaking in a code diff:
- Default user changes. Root to non-root is the classic: better
for everyone, and it breaks every deployment writing to a path the new user
can’t. Announce it a release ahead, with the exact
runAsUser/volume-permission fix. - Entrypoint and command changes. Anything that overrides
command:or wraps the entrypoint in an init system breaks quietly. - Base distro switches. Alpine to distroless removes the shell β
and with it every
kubectl execdebug session and every shell-based healthcheck. Musl/glibc moves change what native extensions load. A distro switch is always a headline entry, never a bullet. - Env var defaults, exposed ports, volume and config paths. The things manifests reference by name. Renaming a config mount point is an API removal in every practical sense.
- Dropped architectures. Removing arm64 from the manifest list
turns someone’s entire cluster into
exec format error. Dropping an arch needs the full deprecation treatment; adding one is a feature worth announcing. - Size jumps. Not breaking, but a 40MB image becoming 400MB changes pull times, node disk pressure, and cold-start latency. State the delta and the reason.
One release, many tags: the variant matrix
Real images ship variants β 1.4.2,
1.4.2-slim, 1.4.2-alpine,
1.4.2-bookworm, each a multi-arch manifest. Readers holding
one variant need to know two things your git history won’t tell them: which
variants this entry applies to, and what each tag resolves to now. The fix is
mechanical: write one entry per release, scope variant-specific notes inline
(“alpine variant only: musl bumped to 1.2.5”), and close the entry with
the tag→digest map for the release. That map is also your silent-re-push
insurance: it turns “did this tag move?” from archaeology into a text
search. If the variant list itself changes β a new slim build, bookworm retired β
that’s an entry of its own, with dates, per the
sunset playbook.
Where the notes live (registries won’t help you)
Here’s the uncomfortable survey: no major registry has a changelog surface. Docker Hub gives you one mutable description blob for the whole repository β no per-release anything. GHCR renders even less. Quay, ECR Public: the same. So the two-layer pattern that solves app stores solves registries too, except the store layer here is nearly empty:
- A canonical changelog page with dated entries and stable permalinks, linked prominently from the registry description. This is the only layer you fully control.
- OCI labels that point home. Bake
org.opencontainers.image.url,.source,.versionand.revisioninto every build, and point.url(or a dedicated label) at the changelog. Labels travel inside the artifact β anyone holding the image candocker inspecttheir way to your notes, which no registry description can promise. - GitHub Releases, if your source is public β not for humans, for robots: Renovate embeds release notes into the bump PRs it opens, which puts your words in front of the person clicking merge. The open-source guide covers keeping Releases mirrored from a canonical source instead of hand-maintained twice.
- A machine-readable feed. Fleet operators who watch vendor images (the discipline in vendor changelog monitoring) need RSS/JSON, not a description blob β see machine-readable changelogs for the structural rules that make a feed automatable.
Write for the automated updater
A large share of your “readers” are Renovate, Dependabot, and Watchtower-style auto-pullers. The humans appear at exactly one moment: the bump PR. That entry gets the same 30-second read as a library dependency bump, so the same rules apply β verdict first (“safe to bump: rebuild only, no behavior changes” or “Breaking: container now runs as UID 10001 β see migration”), symptoms not internals, migration steps inline. Watchtower users never see a PR at all: for them, your changelog is the only warning system that exists, which is one more reason breaking changes in images deserve a release of deprecation lead time, not a morning surprise.
Anti-patterns
- Latest-only publishing. One rolling tag, no version tags, no history. Consumers can’t roll back, can’t pin, can’t tell what changed β the image equivalent of editing prod in place.
- The silent tag move. Re-pushing an existing version tag with different contents and telling nobody. Detected eventually, trusted never again.
- Notes only in the source repo. Image users pull from a
registry; many never visit the repo and some can’t find it (that’s
what the
.sourcelabel is for). Meet them where the artifact is. - “Security rebuild” with no CVE list. Unverifiable reassurance. Scanners name the CVEs; your entry should too.
- Dropping an architecture or variant silently. The people it breaks are by definition the ones not reading your Slack.
- Variant soup. Six suffixes, no table saying how they differ or which one is recommended. Every variant you publish is a support surface; document the matrix or shrink it.
Where Wakelog fits
Wakelog is a good home for the canonical layer. The publish step
is one curl, so the same CI job that pushes the image posts
the entry β digest map and all β the pattern from the
CI automation guide. Scheduled CVE
rebuilds can post with the security tag so operators can
filter for exactly those; every entry gets a stable permalink you can bake into an
OCI label; and the page ships RSS and JSON feeds plus a list API for the fleet
operators and scripts watching you. Honest caveats: Wakelog never inspects your
images β it won’t notice a digest change or diff layers for you (pair it with
your registry’s webhook or a scanner for detection); it publishes what your
pipeline tells it, nothing more.
Start your image changelog — free Next: IaC module changelogs →
Related guides
- Infrastructure-as-code changelogs: Terraform modules, Helm charts, and changes that touch running systems
Blast-radius versioning, the expected-plan-diff line, moved blocks and values mapping tables, the Helm CRD gap, and release notes that survive the 30-second Renovate-PR review. - On-prem and self-hosted release notes: writing for operators who upgrade on their own schedule
Upgrade paths and change windows, the operational facts every entry needs, LTS branches and backports, CVE ranges scanners can read, and notes that survive an air gap. - Security advisories vs changelog: how to publish a security fix
A security fix needs two write-ups on two different clocks: the ship-day changelog entry and the full advisory. What goes in each, when a CVE is worth it, and why silent patching always backfires.
Last updated 2026-07-31 Β· All guides