Renovate PR #76 (merged 2026-06-06 10:48) bumped ghcr.io/immich-app/postgres
from `16-vectorchord0.3.0` to `17-vectorchord0.3.0`. PostgreSQL major
upgrades require pg_upgrade or pg_dump/restore — the new server binary
refuses to open a data directory initialized by the previous major:
FATAL: database files are incompatible with server
DETAIL: The data directory was initialized by PostgreSQL version 16,
which is not compatible with this version 17.6
Both immich-postgres and immich-server (depends on Postgres) went into
CrashLoopBackOff. PVC still holds the v16 datadir.
This PR:
1. Reverts ghcr.io/immich-app/postgres back to `16-vectorchord0.3.0`
so immich recovers immediately.
2. Adds a packageRule with `dependencyDashboardApproval: true` covering
`postgres`, `postgis/postgis`, and `ghcr.io/immich-app/postgres`.
Any update to these images is now held on the Dashboard's "Pending
Approval" section -- Renovate won't even open a PR until the user
explicitly ticks the box. Forces the migration plan to be made
BEFORE the change reaches main.
This is the same recovery pattern we just used for meilisearch (PR #77)
-- a class of stateful images where the on-disk format isn't
forward-compatible across version bumps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Renovate PR #32 (merged 2026-06-06 09:30) bumped getmeili/meilisearch
from v1.11.3 to v1.45.2 under the default-allow + 3-day stability rule.
Meilisearch's on-disk index format is NOT forward-compatible across
that range; wanderer-meilisearch went into CrashLoopBackOff with:
Error: Your database version (1.11.3) is incompatible with your
current engine version (1.45.2).
The PVC still holds the v1.11.x index, so the safest immediate recovery
is reverting the image tag. Wanderer's search starts working again the
moment the pod comes up on v1.11.3.
To prevent recurrence, add a packageRule that holds ALL meilisearch
updates behind the dashboard's "Pending Approval" checkbox via
`dependencyDashboardApproval: true`. PRs won't be opened until the
user explicitly approves them on the dashboard, so the version bump
can be planned around the documented dump/restore migration path
(https://www.meilisearch.com/docs/learn/update_and_migration/updating).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Verified with the GitHub Releases API for Termix-SSH/Termix that the
actual `tag_name` field is `release-X.Y.Z-tag` (with a `-tag` suffix),
even though the release `name` is `release-X.Y.Z`. Renovate's
github-releases datasource keys off `tag_name`, so the regex versioning
correctly rejects all candidates as invalid:
INFO: Found no results from datasource that look like a version
(dependency=Termix-SSH/Termix)
The docker image at ghcr.io/lukegus/termix uses the short form
(`release-X.Y.Z`, no suffix), which is what the manifest also has.
Fix: add `extractVersionTemplate: ^(?<version>release-\d+\.\d+\.\d+)`
which Renovate applies to each candidate from the datasource BEFORE
the versioning regex sees it. tag_names `release-2.3.2-tag` become
`release-2.3.2`, the regex versioning parses them, comparison works,
and Renovate writes the short form back to the manifest -- which is
the correct tag at the ghcr.io registry.
(extractVersion is NOT applied to currentValue, but currentValue already
is in the short form, so no normalization needed there.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Third attempt. Debug run confirmed `loose` + `extractVersion` STILL produces:
DEBUG: Dependency Termix-SSH/Termix has unsupported/unversioned value
release-1.11.0 (versioning=loose)
DEBUG: Skipping Termix-SSH/Termix because no currentDigest or pinDigests
`extractVersion` is only applied to CANDIDATE versions (from the datasource),
not to currentValue. Renovate's pre-validation runs the raw `release-1.11.0`
through `loose`, which can't parse it (the `release-` prefix breaks semver
detection), so Renovate falls back to digest-only and gives up.
`regex` versioning is the only mode that parses the prefixed value directly
(no extractVersion needed) — Renovate's regex.matches() accepts `release-1.11.0`
because the rule's pattern captures the whole tag. github-releases datasource
returns the upstream `release-X.Y.Z` tag_names which the same regex parses.
No conversion needed; the new tag written back to the manifest is the same
`release-X.Y.Z` form, valid in the ghcr.io/lukegus/termix registry.
Removes extractVersionTemplate (no longer needed).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous attempt (inline `# renovate:` comment in termix.yaml) silently
did nothing -- after merge + manual run, the dashboard's
`termix-system/termix.yaml (2)` was the resource count (Deployment +
Ingress), not detected updates. No PRs opened, no termix branches, no
queue entries anywhere.
Root cause: Renovate's `kubernetes` manager does NOT process inline
`# renovate:` comments. Those work for dockerfile/flux/helmfile/github-
actions/helm-values/etc., but kubernetes is missing from that list.
Correct fix: a `customManagers.regex` entry that extracts termix's image
directly with the right datasource/versioning/extractVersion set at
EXTRACTION time -- before any docker-version pre-check can reject the
prefixed tag. Plus a packageRule disabling the kubernetes manager for
termix so it doesn't silently skip the dep and clutter the dashboard.
Changes:
- admin-system/renovate.yaml:
* enabledManagers += "custom.regex"
* customManagers: termix.yaml regex extraction -> github-releases
datasource on Termix-SSH/Termix with `extractVersion=^release-(?<version>.+)$`
* packageRules: disable kubernetes manager for ghcr.io/lukegus/termix
- termix-system/termix.yaml: drop the useless inline comment, leave a
NOTE explaining where the actual config lives.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Debug-level dry-run revealed why the previous packageRule approach
(`datasource: github-releases` + `packageName: Termix-SSH/Termix` +
`versioning: regex:^release-...`) silently did nothing:
DEBUG: Dependency ghcr.io/lukegus/termix has unsupported/unversioned
value release-1.11.0 (versioning=docker)
DEBUG: Skipping ghcr.io/lukegus/termix because no currentDigest or
pinDigests
The kubernetes manager extracts the image with the default versioning
(=docker), runs an EARLY currentValue pre-check, fails on `release-1.11.0`,
falls back to digest-based updates, and gives up -- all BEFORE the
packageRule's `versioning` override has a chance to apply. Same failure
class as the earlier `extractVersion` attempt.
Renovate's documented fix for this exact case is an inline manifest
comment that applies overrides at extraction time:
# renovate: datasource=github-releases depName=Termix-SSH/Termix \
# versioning=loose extractVersion=^release-(?<version>.+)$
image: ghcr.io/lukegus/termix:release-1.11.0
With extractVersion stripping the `release-` prefix at extraction, the
loose semver parser handles the resulting `1.11.0` / `2.3.2` fine.
github-releases datasource gives Renovate real upstream timestamps so
the 3-day stability gate works normally. Removing the now-superseded
packageRule keeps the config clean (6 rules instead of 7).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
26 items sat in dashboard "Rate-Limited" after the first default-allow
run (Sat 02:00); at 8 PRs/run + 1 run/week the backlog would take ~3
weeks to drain. Doubling to 16/16 cuts that to ~2 runs while still
leaving headroom (the dashboard "Pending Approval" majors and ghcr.io
"Pending Status Checks" don't count against this limit anyway).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Was Sun 04:00; now Sat 02:00 so Renovate's wave lands at the start of
the weekend instead of the end. If an auto-merged update breaks
something, Viktor has the full weekend to troubleshoot.
`0 2 * * 6` = Saturday 02:00 in Europe/Budapest (the CronJob already
sets timeZone, so this is wall-clock local).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the security-flagged `minimumReleaseAge: 0` bypass with a
proper datasource swap.
Why: ghcr.io OCI manifests for ghcr.io/lukegus/termix don't expose a
release timestamp, so Renovate's default `timestamp-required` mode
holds updates indefinitely. The previous fix (zeroing the gate) was
flagged as a supply-chain control regression -- correctly, since it
weakens the stability protection for that package.
Cleaner fix: point Renovate's version lookup at the upstream GitHub
Releases (Termix-SSH/Termix per the OCI source label) where timestamps
ARE published. The 3-day gate then works for termix the same way it
works for other packages with intact timestamps. Renovate still
updates the same image -- the manager extracts ghcr.io/lukegus/termix
from termix.yaml and writes the new tag back; only the version-source
lookup is redirected. The ghcr.io registry hosts every release-X.Y.Z
tag (verified release-2.3.2 present), so the writeback target stays
valid.
Major bumps (1.x -> 2.x) continue to queue for dashboard approval via
the global major rule.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Last commit's global `minimumReleaseAgeBehaviour: timestamp-optional` did
two unwanted things:
1) Dry-run showed 0 "Would commit" branches (was 33 before). The flag
appears to alter Renovate's filtering more broadly than expected and
is not the right knob here.
2) Automated security review correctly flagged the global form as
fail-open: a missing timestamp on ANY package would bypass the
stability gate, weakening supply-chain protection across the fleet.
Narrow fix instead:
- Revert the global setting (back to default `timestamp-required`).
- Add `minimumReleaseAge: "0 days"` ONLY to the termix packageRule.
ghcr.io OCI manifests for ghcr.io/lukegus/termix don't expose a
release timestamp Renovate can read, so the global 3-day gate would
otherwise hold updates indefinitely (this is the same class of issue
that's been keeping reloader/homepage/headlamp on "Pending Status
Checks" for 8+ days). Major bumps still gated by the global major
rule (`dependencyDashboardApproval: true`).
Other ghcr.io packages with the same issue (reloader, homepage, headlamp)
remain on the dashboard's "Pending Status Checks" list and can be
force-approved per-update via the checkbox UX. That's a slower but safer
manual-approval path that preserves the supply-chain gate's intent.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Debug dry-run revealed why termix (and reloader/homepage/headlamp
8d ago) sit in "Pending Status Checks" indefinitely:
Marking 2 release(s) as pending, as they do not have a
releaseTimestamp and we're running with
minimumReleaseAgeBehaviour=timestamp-required
"depName": "ghcr.io/lukegus/termix"
"versions": ["release-1.11.2", "release-1.11.1"]
"check": "minimumReleaseAge"
ghcr.io OCI manifests for these images don't expose a release
timestamp Renovate can read, so the default `timestamp-required`
mode turns the 3-day stability gate into an INFINITE hold for
ghcr.io packages -- silently. PRs are never opened.
Switching to `timestamp-optional` (other supported value per Renovate
source: lib/config/options/index.ts) makes the gate best-effort: the
3-day window is still enforced for any package the datasource gives a
timestamp for; packages without a timestamp are allowed through.
Restores intended behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Debug-level dry-run showed:
Dependency ghcr.io/lukegus/termix has unsupported/unversioned value
release-1.11.0 (versioning=loose)
Skipping ghcr.io/lukegus/termix because no currentDigest or pinDigests
`versioning: loose + extractVersion` doesn't work as intended here:
Renovate evaluates the currentValue (`release-1.11.0`) against the loose
parser BEFORE extractVersion is applied. loose can't parse a prefixed
value, so Renovate falls back to digest-based comparison; we don't pin
digests, so it silently skips and no PRs are ever opened. (Upstream has
v1.11.1, v1.11.2, and a major bump to release-2.3.2 since we deployed.)
Fix: use `versioning: regex:^release-(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$`
which parses the whole tag including the `release-` prefix. The named
major/minor/patch groups let Renovate categorize bumps correctly so
the existing minor/patch automerge and major dashboard-approval rules
apply normally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Grows the Renovate pilot from 4 apps to a 16-app Tier 1 allowlist of
low-risk leaf apps (no DBs / schema migrations). packageRules keeps the
same 4-rule shape (default-deny, enable, automerge-minor/patch,
major-dashboard-approval) with the expanded package list in all three
Tier 1 rules.
Behavior changes:
- minimumReleaseAge "3 days" on the automerge rule: Renovate won't open
a minor/patch PR until the tag has been published upstream for 3 days
(stability gate; chosen over branch protection, which would disable
automerge entirely).
Image-string corrections vs. the planned list (Renovate matches the
exact image as written in the manifest; verified against the YAML):
- homepage -> ghcr.io/gethomepage/homepage (had no registry)
- reloader -> ghcr.io/stakater/reloader (had no registry)
- termix -> ghcr.io/lukegus/termix (had no registry)
Notes:
- registry.k8s.io/kube-state-metrics/kube-state-metrics is kept in the
list but currently matches nothing: ksm has no image in this repo
(only a Prometheus scrape target), so it's a harmless no-op until ksm
is ever deployed via a manifest here.
- ghcr.io/lukegus/termix uses a non-semver tag (release-1.11.0); watch
whether Renovate categorizes its updates as minor/patch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Self-hosted Renovate as a weekly CronJob (Sun 04:00 Europe/Budapest)
opening dependency-update PRs against admin/homelab-manifests on Gitea.
Pilot is deliberately narrow:
- Only the kubernetes + helm-values managers are enabled.
- Default-deny packageRule; only four images may update:
opengist, uptime-kuma, gokapi, cal.com.
- minor/patch -> PR with Gitea native auto-merge (platformAutomerge).
- major -> held for manual approval via Dependency Dashboard checkbox.
Image pinned to renovate/renovate:43.197.0 (the plain tag is the
minimal image; the -slim suffix was retired upstream after v37.440.x).
Stateless: no Service/Ingress/PVC. Read-only root FS with a 2Gi /tmp
emptyDir for git clones + cache. Secrets from existing renovate-secrets.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>