0004. :latest tag remediation plan¶
- Status: accepted
- Date: 2026-04-15
- Deciders: lusoris
Decisions recorded 2026-04-15¶
- Hotio carve-out approved — hotio
*arrapps may use:release(curated stable stream). PRINCIPLES.md updated to recognise curated-stable tags as an exception to the "no mutable tags" rule, provided the exception is referenced from an ADR. - Automation = Renovate — not argocd-image-updater. Covers Helm charts,
Docker tags, Terraform modules in one tool.
renovate.jsonlands alongside this ADR. - Pass 1 defers to Renovate — no hand-pinning of vaultwarden / qbittorrent+gluetun / stashapp / jellyfin. Let Renovate open its first PR stream against these; review + merge as the normal cadence.
Context¶
CLAUDE.md / PRINCIPLES.md state: "All images and charts pinned (no latest)." Pre-commit has a no ':latest' image tags hook that enforces this — but the hook only runs on changed files, so pre-existing violations are grandfathered and invisible during normal development.
Full audit on 2026-04-15: 21 values.yaml files, 51 tag: latest occurrences. Breakdown by tag ecosystem:
| Ecosystem | Files | Typical upstream tag convention |
|---|---|---|
ghcr.io/hotio/* (arr stack) |
9 (sonarr ×3 + radarr ×2 + lidarr + prowlarr + sabnzbd + seerr) | :release (stable) / :testing / :nightly — rolling. Semver-specific tags exist (e.g., sonarr:5.0.4.2059) but hotio recommends :release. |
linuxserver/qbittorrent |
1 | semver: :5.2.0-r0-ls403 |
stashapp/stash |
1 | semver: :v0.28.2 |
jellyfin/jellyfin |
1 | semver: :10.11.1 |
alpine (init/sidecar) |
4 (jellyfin, stashapp, newsblur ×3) | major.minor: :3.22 |
newsblur/* |
4 | hash-only (no semver releases). Requires image-digest pin. |
curlimages/curl |
1 (bandwidth-scheduler) | semver: :8.11.0 |
quay.io/minio/{minio,mc} |
4 (minio ×2, minio-home ×2) | date: :RELEASE.2025-04-03T14-56-28Z |
ghcr.io/dani-garcia/vaultwarden |
1 | semver: :1.34.1 |
ghcr.io/cleanuparr/cleanuparr |
1 | semver: :v2.0.4 |
ghcr.io/unpackerr/unpackerr |
1 | semver: :0.14.2 |
pcjones/umlautadaptarr |
1 | semver: :1.7.5 |
ghcr.io/raydak-labs/configarr |
1 | semver: :1.9.3 |
qmcgaw/gluetun |
1 (in qbittorrent) | semver: :v3.40.0 |
prometheus-qbittorrent-exporter |
1 | semver: :v1.6.0 |
Decision (proposed)¶
Remediate in three passes, prioritising by risk and by how much version drift matters.
Pass 1 — internet-facing + security-adjacent (do first)¶
One commit per app; smoke-test after each:
vaultwarden— password vault; a surprise rolling update is actively dangerous.qbittorrent+gluetun— public egress via gluetun WireGuard; tunnel config changes between gluetun majors break kill-switch.stashapp— login/auth stack; sensitive data.jellyfin— auth + direct-play codecs; transcoder changes hit GPU passthrough.
Pass 2 — hotio/*arr stack¶
Script-driven: generate one PR that replaces every hotio tag: latest with
:release (not semver). Rationale: :release is the upstream-curated stable
stream; using semver introduces manual bump burden on 7 apps. :release is
still mutable but is the hotio convention and is what our workloads have been
running anyway. We explicitly accept the mutable-tag trade-off for hotio
because the alternative (pinning 7 arr apps to 7 different semvers each minor
update) is worse operational overhead.
Note: this still technically violates the hard rule. Two options to resolve:
- A. Relax the rule: "no
:latest; curated-stable streams like hotio:releaseare OK with an ADR reference." - B. Bite the bullet and pin to semver, manage via renovate or argocd-image-updater.
Recommend (A) for hotio specifically; (B) for everything else.
Pass 3 — everything else¶
Semver pins per the table above. For newsblur/* (no semver tags), pin by sha256 digest — image@sha256:… is the escape hatch.
Consequences¶
Positive:
- Closes a widely-broken hard rule.
- Surfaces real image state in values.yaml instead of relying on registry-side mutability.
- Makes CVE response tractable (know exact version per workload).
Negative:
- 21 individual changes to test. Over a weekend, reasonable; in one session, not.
- Introduces manual version bumps for ~10 apps unless we land renovate/argocd-image-updater config.
- Hotio
:releaseexemption (if adopted) requires updating PRINCIPLES.md / pre-commit hook to recognise the carve-out.
Open questions¶
- Renovate vs argocd-image-updater: the repo already has
docs/argocd-image-updater/(upstream mirror). Did we start adopting it? If yes, finish; if no, evaluate renovate as a simpler alternative. - Hotio carve-out (option A): merge to main? Or pin hotio to semver too and eat the bump overhead?
- Pre-commit hook strengthening: add a second hook that runs on all values.yaml files (not just changed) with
--hook-type=pre-pushso new violations can't sneak in even via unrelated commits.
Implementation order¶
- Write & land the renovate config (or argocd-image-updater) — blocks unless we're OK with manual bumps.
- Pass 1 apps (4 files).
- Pass 2 hotio decision — either script-pin to
:releaseor hand off to renovate. - Pass 3 — the remaining long tail; can be batched into 2–3 PRs.
- Add pre-push hook that rejects any
tag: latestanywhere underapps/.