Renovate auto-merge policy
Renovate runs on the gitea-runner and opens pull requests for everything pinnable in this repo — container images, Helm charts, Flux components, Tofu providers, Gitea Actions workflow versions, npm packages in the docusaurus site. Most of them never need a human's eyes. A few must always have one. This page is the policy that draws the line.
The principle
Auto-merge anything that can't meaningfully change behaviour without changing the version number it tells you. Hold anything that can.
The shape of the change matters more than the size:
| What changes | Auto-merge? | Why |
|---|---|---|
Image digest on a pinned image: foo/bar@sha256:… | yes | Same tag, upstream re-pushed — usually a security backport or rebuild |
Image patch tag (foo:1.2.3 → foo:1.2.4) | yes | Semver promise; if the upstream lies the snapshot tier catches the fallout |
Helm chart patch version (e.g. cilium 1.19.2 → 1.19.3) | yes | Same as patch tags above |
Image minor tag (foo:1.2.x → foo:1.3.x) | no | Semver permits new features, possible new requirements (CRDs, config) |
| Helm chart minor version | no | Charts routinely change values defaults in minor bumps |
| Major version of anything | no | The upstream is telling you to read the changelog |
| New CRD-introducing chart bump (regardless of semver) | no | Need to apply the new CRDs and verify in-cluster |
kustomization.yaml components: references | no | A new component shape can silently change app behaviour |
renovate.json itself | no | Self-modifying CI must always be reviewed |
The auto-merge rule is encoded in renovate.json as a packageRules clause keyed on matchUpdateTypes: ["digest", "pin", "patch"]. Everything else falls through to "needs human review."
Why digest pinning makes auto-merge safer, not riskier
Counter-intuitive at first: digest-pinning images means more PRs (every silent rebuild upstream becomes its own diff) and more of them auto-merge. But that's exactly the safety:
- A digest PR is a high-signal event. "This
1.2.3image is now a differentsha256:…" tells you upstream did something — even if they didn't bump the tag. Without digest pinning that change is invisible. - The PR carries the previous and new digest in the diff, so a forensic search after a bad week ("when did this start regressing?") has a real history to grep.
- Renovate writes the new digest as an annotation on the PR, so the auto-merge log is self-explanatory.
The downside — extra PR volume — is purely cosmetic. Auto-merge clears them as fast as they come.
What auto-merge actually does
Renovate opens PR
│
▼
gitea-runner CI:
- kustomize build (every kustomization renders cleanly)
- kubectl --dry-run=server (every manifest is valid against the API)
- schema check
│
▼ passes
Auto-merge?
├── yes → fast-forward merge on green
└── no → PR sits open, assigned to a human
A failing CI run blocks the merge regardless of policy. There's no "force-merge anyway" path — that would defeat the point.
The auto-merge merges to main. From there, the GitOps flow takes over: Flux pulls the new commit, reconciles, the cluster rolls. Auto-merge is not an auto-deploy — it's a manifestation of "the manifests in main are the source of truth."
What gets reviewed by a human
Two flavors of PR sit open until a human reads them:
The expected ones — minor / major bumps. These are usually a one-page CHANGELOG read and an "approve" click. Sometimes a chart minor bump needs a config tweak in the values; the PR diff makes it obvious.
The surprising ones — anything Renovate didn't expect to be in scope (a fresh dependencies: block, an unfamiliar _kustomization path, a chart introducing a new CRD). Renovate's "Are these still relevant?" comments are usually the entry point for an investigation rather than a merge.
The unstated rule: if a PR has been open for more than a few weeks, it's either do it or delete it (with a packageRule that excludes it). PRs that linger become permanent noise.
Frequency tuning
Renovate runs hourly inside the cluster. The volume per week:
- ~30 digest / patch PRs (auto-merged)
- ~5 minor PRs (need a 60-second look)
- ~1 major PR (need an actual read)
That's the right cadence for a homelab. Larger codebases would need group rules (bundle the patch updates per chart), but at this scale individual PRs are easier to audit.
What auto-merge does NOT cover
- Manifests that aren't dependency-pinned at all — e.g. a hand-written
values.yamlthat saysapiVersion: helm.toolkit.fluxcd.io/v2. Renovate doesn't see these, doesn't bump them, can't auto-merge them. - OS-level package versions on host machines — the Ansible-managed Docker hosts, Talos image versions, Proxmox VE versions. These are out of Renovate's scope; tracked manually.
- Code in the repo itself — the docusaurus site, the
tofu/modules/, the generator scripts. These are normal pull requests from a human.
The audit trail
The catch with auto-merge is "I don't know when this changed." The audit trail mitigates that:
- Gitea's commit log is the source of truth — every auto-merged PR is a commit with the Renovate bot as author and the bump in the message.
- Flux events show when a chart re-rolled —
kubectl events -n flux-system | grep <controller>correlates a HelmRelease rev with a commit. - Gatus spots customer-visible regressions in the minutes after a rollout; if a deploy goes bad, the chronological order of the Gatus alert and the most recent merge is usually enough to point at the culprit.
A bad auto-merge gets reverted by a PR with the inverse diff. Renovate notices the revert and stops re-opening that bump for a configured cooldown (stabilityDays). The reverted version stays in the repo's history, available for the next time it's safer to try.
When to tighten the policy
The policy should tighten — fewer auto-merges, more reviews — when:
- An upstream consistently breaks patch semver (image keeps shipping incompatible binaries with no tag change).
- A controller becomes load-bearing for something irreversible (an in-flight migration, a CRD-only upgrade path).
- Operator vacation. Stop auto-merging anything for the week — set
enabled: falseon the relevantpackageRules.
The policy should loosen — more auto-merges — when:
- Confidence in a particular upstream is high and a digest-bump backlog is forming. Promote that upstream to auto-merge digest only.
Either direction is one packageRules edit in renovate.json, scoped narrowly enough to revert if the experiment fails.
See also
- Apps → Gitea Runner — where Renovate actually runs
- Topics → GitOps flow — what happens after the auto-merge lands on
main - Topics → Disaster recovery drill — the safety net for the unlikely bad-week bypass