Files
home-network-k3s-impl/decisions/2026-10-30-pin-pihole-jackett-plex.md
T
jakman85andClaude Opus 5.5 183bca6897
gate / gate (pull_request) Successful in 2m57s
gate / conformance (pull_request) Has been skipped
feat(images): pin pihole, jackett and plex to the digests already running; Renovate follows them
pihole/pihole (no tag)               -> 2026.10.0               sha256:d3413471...
lscr.io/linuxserver/jackett:latest   -> v0.24.2865-ls7          sha256:4fcf3cc2...
plexinc/pms-docker:latest            -> 1.43.4.10903-e5521bd8c  sha256:e0ab2739...

Each tag was resolved against its registry and matched the pods' imageID, so the image is
unchanged (the pod template string does change, so Flux rolls the three workloads).

renovate.json5: regex versioning for jackett (-lsN) and plex (build-hex); jackett is looked
up on ghcr.io via overridePackageName, because lscr.io is capped at dockerMaxPages=20 pages
and jackett's 32k tags put the running one at #31,980 (dry run proposed nothing without it);
jackett weekly (13 builds in 12 days); pihole deliberately unheld, year rollover = major.
Stale "publishes only latest" prose fixed in renovate.json5 and three runbooks.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 10:38:07 -05:00

97 lines
7.0 KiB
Markdown

---
date: 2026-10-30
branch: feat/pin-pihole-jackett-plex
title: Pin pihole, jackett and plex to the digests already running, and make Renovate able to follow them
---
## What changed
- **`catalog/{pihole,jackett,plex}.yaml` `source.image`** pinned to the tag whose digest was
already running, so the *image* does not change:
- `pihole/pihole` (no tag) -> `pihole/pihole:2026.10.0`
- `lscr.io/linuxserver/jackett:latest` -> `lscr.io/linuxserver/jackett:v0.24.2865-ls7`
- `plexinc/pms-docker:latest` -> `plexinc/pms-docker:1.43.4.10903-e5521bd8c`
- **`apps/wave-0/{pihole,jackett,plex}.yaml`** re-rendered (`render.py --all catalog -o apps
--wave-layout --no-image-automation`, in the toolbox). The diff is exactly the three app-container
`image:` lines; nothing else moved.
- **`catalog/jackett.yaml`** `update.range` comment: "stay on the Jackett 0.22.x stable line" was
false (0.24 runs, and `>=0.22.0` has no ceiling). Range unchanged; comment corrected.
- **`renovate.json5`**, four packageRules plus a comment block:
- jackett `versioning: regex:^v(major).(minor).(patch)-ls(build)$` (Renovate's linuxserver example).
- jackett **`overridePackageName: ghcr.io/linuxserver/jackett`**: not in the brief, and needed.
Renovate's docker datasource reads at most `dockerMaxPages` (20, a global-only option) tag
pages from any host except a short list that includes ghcr.io (1000 pages). lscr.io is not on
that list. Jackett has **32,093** tags, oldest first, ~1000 per page; the running tag is #31,980.
Before this rule the dry run extracted jackett at `v0.24.2865-ls7` and **proposed nothing**,
with no warning. The override changes only the lookup; the manifests keep `lscr.io/...`.
Every jackett rule matches on `depName`, because the override rewrites `packageName`.
- jackett `schedule: after 3am and before 6am on sunday`, i.e. weekly. Measured: 13 ls builds in
12 days (ls7 built 2026-10-17, ls20 built 2026-10-29). Each is a `patch` and auto-merges, so on
the daily window it would mean a PR, a gate run and a jackett restart (Recreate) every day.
Weekly picks the newest tag, so it skips nothing. `catalog/jackett.yaml update.cadence` still
says daily. That field fed the retired Flux image-automation, and the rule's description
says the rule is now the cadence that takes effect.
- plex `versioning: regex:^(major).(minor).(patch).(build)-[0-9a-f]+$`.
- pihole: **no rule**, as the operator decided. CalVer parses under default docker versioning.
Patches and minors auto-merge, and a year rollover is a major (dashboard approval). This is
written in a comment.
- Corrected the "WHAT RENOVATE STILL CANNOT FIX" comment. It listed these three as floating;
they publish immutable version tags.
- No `allowedVersions` mirror was added. None of the three catalog ranges has a ceiling
(`>=2024.0.0`, `>=0.22.0`, `>=1.40.0`), so #86's mirror has nothing to encode for them.
- **Runbooks:** `image-policy-stalled.md` no longer says jackett and pms-docker are
`latest`-only. In `plex-quicksync.md`, "the floating-image caveat" became "the image-version
caveat", because the deployment no longer runs `:latest` with an implicit `Always`, and the symptom
is now "after a pms-docker bump". `plex-claim.md`'s two "running `:latest`" mentions are
now in the past tense.
## What I verified
- **Live digests** (read-only `kubectl --context k3s-prod`, pod `.status.containerStatuses[].imageID`):
pihole-0 and pihole-1 `sha256:d34134716fc3…`, jackett `sha256:4fcf3cc22dd3…`, plex
`sha256:e0ab27395614…`.
- **Each chosen tag resolves to the same digest** (anonymous registry HEAD, OCI index + manifest-list
Accept): `pihole/pihole:2026.10.0` = `sha256:d34134716fc3…`; `ghcr.io/linuxserver/jackett:v0.24.2865-ls7`
= `sha256:4fcf3cc22dd3…`; `plexinc/pms-docker:1.43.4.10903-e5521bd8c` = `sha256:e0ab27395614…`.
(For contrast, `pms-docker:latest` is now `sha256:6d778d41…` and `jackett:latest` `sha256:ee6ae915…`.
The pods would have drifted on their next restart.)
- `renovate-config-validator` (ghcr.io/renovatebot/renovate:44.167.0) reports "Config validated
successfully". The only warning is the existing `docker` block migration.
- **Renovate dry run** (`RENOVATE_PLATFORM=local`, `--dry-run=full`, 44.167.0, anonymous, includePaths
limited to the six files). It extracted all three from both `catalog/` (custom regex) and
`apps/wave-0/` (kubernetes) under the same depName, so each lands in one branch:
- plex `1.43.4.10903-e5521bd8c` -> `1.43.5.11079-b3b13e506` (patch, `renovate/plexinc-pms-docker-1.x`)
- jackett `v0.24.2865-ls7` -> `v0.24.2961-ls20` (patch, `renovate/lscr.io-linuxserver-jackett-0.x`).
This needs the ghcr override; without it there was no proposal.
- pihole `2026.10.0` -> none. That is correct, because 2026.10.0 is the newest numbered tag on Docker Hub.
- **Automerge, schedule and dashboard flags** come from Renovate's own `applyPackageRules`
(datasource-merge, then update-type-merge, the same order as `flatten.js`), run on the resolved
config. The local platform does not run the branch worker:
- plex patch: automerge **true**, daily window.
- jackett patch: automerge **true**, schedule **sunday** window.
- pihole (synthetic updates) `2026.10.1` patch and `2026.11.0` minor: automerge **true**. `2027.01.0`
major: automerge **false**, `dependencyDashboardApproval` **true**, +needs-review/breaking.
- **Versioning filters** (Renovate's versioning module): the jackett regex rejects `latest`, bare
`0.24.2961`, `amd64-…` and `development-…`. The plex regex rejects `latest`, `public`, `beta`,
`plexpass` and `…-amd64`/`…-arm64`. Docker versioning ranks `2026.09.0 < 2026.10.0`.
- Touched paths (`catalog/`, `apps/`, `renovate.json5`, `runbooks/`, `decisions/`) are not in
`infra/gitea/approval-policy.json` `sensitive_paths`.
## What I did NOT verify
- **That the merge is restart-free. It is not.** The pod-template `image` string changes, so Flux
rolls all three: plex and jackett are `Recreate`, so each has a brief outage, and pihole is a
2-replica StatefulSet that rolls one pod at a time. The digest is identical, so the image's
behaviour should not change, but the kubelet resolves the new tag name and may pull it. The
rollout itself was not observed.
- The implicit `imagePullPolicy` moves from `Always` (`:latest` or no tag) to `IfNotPresent`. This is
intended, and was not observed live.
- A real (non-dry) Renovate run against Gitea, and whether the Sunday window actually opens a jackett
PR. The schedule was only evaluated, not exercised.
- Other `lscr.io/linuxserver/*` images (radarr, sonarr, qbittorrent) may hit the same 20-page
truncation once their tag counts grow. Not measured here, and not changed.
- Plex not self-updating on a version tag was checked only against the running pod (read-only
`kubectl exec cat`). `/etc/cont-init.d/50-plex-update` does `exit 0` when `version` from
`/version.txt` equals the installed `plexmediaserver` package. Both are
`1.43.4.10903-e5521bd8c`. I did not boot a pod on a *new* tag to watch that short-circuit.