Skip to main content

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 changesAuto-merge?Why
Image digest on a pinned image: foo/bar@sha256:…yesSame tag, upstream re-pushed — usually a security backport or rebuild
Image patch tag (foo:1.2.3foo:1.2.4)yesSemver promise; if the upstream lies the snapshot tier catches the fallout
Helm chart patch version (e.g. cilium 1.19.21.19.3)yesSame as patch tags above
Image minor tag (foo:1.2.xfoo:1.3.x)noSemver permits new features, possible new requirements (CRDs, config)
Helm chart minor versionnoCharts routinely change values defaults in minor bumps
Major version of anythingnoThe upstream is telling you to read the changelog
New CRD-introducing chart bump (regardless of semver)noNeed to apply the new CRDs and verify in-cluster
kustomization.yaml components: referencesnoA new component shape can silently change app behaviour
renovate.json itselfnoSelf-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.3 image is now a different sha256:…" 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.yaml that says apiVersion: 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: false on the relevant packageRules.

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