You’re Manually Bumping Image Versions in Your GitOps Repo. Stop That.
If you’re running Kubernetes the right way, manifests in Git, ArgoCD or Flux syncing them to the cluster, you’ve probably noticed something annoying: new container image releases drop constantly, chart maintainers push updates, and somebody has to go hand-edit those YAML files and commit the changes. That somebody is usually you at 2 AM, wondering if you missed a tag somewhere.
Renovate fixes this. It’s a bot that watches your dependencies (images, charts, base images in Dockerfiles, whatever) and opens PRs when updates are available. You review, test, merge. Automation handles the tedious parts. It’s like hiring a very thorough intern who never sleeps and doesn’t complain about typos.
This isn’t your standard Renovate setup (the bot-versus-bot comparison lives in Renovate vs Dependabot). This is Renovate configured for Kubernetes and Helm specifically, the custom managers, scheduling strategy, grouping rules, and the gotchas that bite you when you’re deploying to a cluster instead of a Node.js app.
Why Renovate Over Dependabot (or Manual)?
Dependabot is GitHub’s native tool, and it caught up on Kubernetes more than most write-ups admit. It ships a helm package ecosystem that reads Chart.yaml dependencies, Chart.lock, and the repository plus tag image pairs inside values.yaml. Its docker ecosystem updates image tags in plain Kubernetes manifests too. The old “Dependabot can’t do Helm” line is stale.
Where it still loses is configurability. Dependabot has no equivalent of a custom regex manager, so a YAML shape it doesn’t recognize is simply invisible. Grouping is limited to the groups key in dependabot.yml, scheduling granularity stops at daily, weekly, or monthly, and there’s no minimumReleaseAge to hold a release back until it has survived a week in public.
Manual updates are reliable until they’re not. You’ll miss a patch, forget to test it, ship a breaking chart change on Friday afternoon, or get burned when a new image version silently drops support for ARM64. There’s no audit trail, no consistency, no automation.
Renovate was built for monorepos and infrastructure repos. It ships five Helm managers out of the box (helmv3, helm-values, helm-requirements, helmfile, helmsman), custom regex managers for YAML shapes nobody standardized, cron scheduling so you don’t get 50 PRs on Monday, and grouping rules to batch related updates. It assumes you can’t test every image version independently, so it gives you the knobs to group them, rate-limit them, and merge them on your terms.
Getting Renovate Running
You have two paths: Mend’s hosted app or self-hosted.
- Mend Renovate Community Cloud (the free plan): connect the GitHub App, point it at your repos, and Renovate runs on Mend’s infrastructure. No compute of your own.
- Self-hosted: Docker container, webhook integration, full control. Overkill for a personal repo, and the config below is identical either way.
For most home lab GitOps setups, the hosted app is the move. Install it from github.com/apps/renovate, authorize your repos, and you’re off. Manage installs and read job logs at developer.mend.io.
The free plan covers an unlimited number of public and private repos. The caps are on throughput, and as of September 2026 they are: one concurrent job per org, active repos scheduled every 4 hours, 1 vCPU and 3 GB RAM per job runner, 15 GB disk, and a 30 minute job timeout. A home lab GitOps repo scans in well under a minute, so none of that will bite you. One thing that will: Mend’s hosted GitLab.com app is offline indefinitely, so GitLab users self-host.
But the real power unlocks when you write a renovate.json config tailored to your setup.
Basic renovate.json for Kubernetes + Helm
Here’s a starter config that handles the essentials, Helm chart updates, container image updates, and scheduling:
{ "extends": ["config:recommended"], "timezone": "America/Los_Angeles", "schedule": ["* 22-23,0-4 * * 1-5", "* * * * 0,6"], "labels": ["renovate", "automation"], "ignorePaths": ["archive/**", "deprecated/**"], "vulnerabilityAlerts": { "labels": ["security", "urgent"], "assignees": ["your-github-username"] }, "packageRules": [ { "matchDatasources": ["docker"], "groupName": "Docker images", "schedule": ["* 22-23,0-4 * * 1-5"] }, { "matchDatasources": ["helm"], "groupName": "Helm charts", "schedule": ["* 22-23,0-4 * * 0,6"] }, { "matchUpdateTypes": ["patch"], "automerge": true, "automergeType": "pr", "automergeStrategy": "squash" } ]}What’s happening here:
extends: ["config:recommended"]: inherit Renovate’s defaults. Don’t reinvent the wheel. (config:baseis the deprecated old name, and the validator will nag you to migrate.)schedule: five-field cron, weekday nights 10 PM to 5 AM plus all weekend. No spam at 9 AM Monday. Renovate also accepts English strings like"after 10pm every weekday", but that’s the deprecated Later syntax and the maintainers no longer take support questions on it. Write cron. The minutes field must be*, because Renovate has no minute granularity.ignorePaths: skip archived or deprecated directories. Renovate scans them otherwise.vulnerabilityAlerts: if a security patch drops, label it and assign it. Note the assignee is a literal platform username. Renovate has no@metoken, and a bogus username fails silently.packageRules: group Docker images together, Helm charts together, different schedules. Patch updates auto-merge (you trust the patches, right?).
Notice what isn’t there: a helm block. There is no manager by that name, and "helm": {"enabled": true} is a config error the validator rejects. config:recommended already enables helmv3 for Chart.yaml dependencies, plus helm-values, helm-requirements, helmfile, and helmsman. Helm works with zero configuration.
This covers 80% of what you need. The remaining 20% is custom managers for edge cases.
Custom Managers: Taming Kubernetes YAML
Before you write a regex, check whether a built-in manager already covers you. Most people reach for a custom manager and then discover Renovate had it handled.
Here’s the classic case: you’re pinning Traefik in a values file like this:
image: repository: traefik tag: 3.6.1The helm-values manager parses exactly that shape. Its problem is the filename. Its default file pattern is /(^|/)values\.ya?ml$/, so it matches values.yaml and nothing else. Widen the pattern and you’re done:
{ "extends": ["config:recommended"], "helm-values": { "managerFilePatterns": ["/(^|/)[\\w-]*values\\.ya?ml$/"] }}One line of config, no regex to maintain. Custom managers earn their keep on shapes no built-in manager knows: an image pinned by tag and digest, a version buried in a ConfigMap, a Kustomize newTag field.
{ "extends": ["config:recommended"], "pinDigests": true, "customManagers": [ { "customType": "regex", "managerFilePatterns": ["/^clusters/.*\\.ya?ml$/"], "matchStrings": [ "image:\\s+(?<depName>[\\w\\-\\./]+):(?<currentValue>[\\w\\-\\.]+)@(?<currentDigest>sha256:[a-f0-9]{64})" ], "datasourceTemplate": "docker", "versioningTemplate": "docker" } ]}What’s going on:
- The regex matches
image: traefik:3.6.1@sha256:..., tag and digest together. datasourceTemplate: "docker": tell Renovate this is a container image, not an npm package.versioningTemplate: "docker", not"semver". Image tags are rarely clean semver.v1.2.3,3.6.1-alpine, and16-bookwormall break the semver scheme, and thedockerscheme handles the prefixes and suffixes.pinDigestssits at the top level, not inside the custom manager. It’s a repository andpackageRulesoption. Nest it undercustomManagersandrenovate-config-validatorrejects the config.
Heads up: recent Renovate renamed fileMatch to managerFilePatterns in custom managers. The old key still works via auto-migration (you’ll just get a “config migration necessary” nag), but if you’re writing a config fresh, use managerFilePatterns and wrap regex values in slashes (/.../), plain strings are now treated as globs.
Custom managers are annoying to debug, so validate before you commit:
npx --yes --package renovate -- renovate-config-validatorThat catches typos and invalid keys. It cannot tell you whether your matchStrings actually matched anything. For that, read the Dependency Dashboard issue Renovate opens in your repo: it lists every dependency Renovate found. If your image isn’t in that list, the regex missed, and staring at the empty PR list won’t tell you why.
Helm Chart Dependencies (The Easy Way)
If you’re using Helm properly, your charts have dependencies defined in Chart.yaml:
dependencies: - name: argo-cd version: "10.7.x" repository: https://argoproj.github.io/argo-helm - name: longhorn version: "1.9.x" repository: https://charts.longhorn.ioGet the chart name exactly right. The argo-helm repo publishes it as argo-cd. Write argocd and helm dependency update fails on a name that isn’t in the repository’s index.yaml, so Renovate has nothing to track. (Versions above are current as of September 2026: argo-cd 10.7.x, longhorn 1.9.x.)
The helmv3 manager picks these up with zero configuration, because config:recommended already enables it. No custom manager needed. It respects your version constraints (10.7.x bumps patch releases but not minor), and it checks the chart exists in the repository index before opening a PR.
Add this to your config to group all Helm updates together:
{ "packageRules": [ { "matchDatasources": ["helm"], "groupName": "Helm chart dependencies", "schedule": ["* 20-23 * * 0"], "reviewers": ["your-github-username"] } ]}Scheduling and Grouping (So You Don’t Wake Up to 50 PRs)
This is where most people trip up. If you don’t schedule and group, Renovate will open a PR for every single version bump, and you’ll have 30 PRs by Monday morning, each one needing individual review and test cycles. That’s worse than doing it manually.
Here’s a smarter approach:
{ "extends": ["config:recommended"], "timezone": "America/Denver", "schedule": ["* 22-23,0-4 * * 1-5", "* 0-6 * * 0,6"], "packageRules": [ { "matchDatasources": ["docker"], "matchUpdateTypes": ["patch", "minor"], "groupName": "Docker image updates", "groupSlug": "docker-updates", "schedule": ["* 20-23 * * 1"], "minimumReleaseAge": "7 days", "automerge": true, "automergeType": "pr", "automergeStrategy": "squash" }, { "matchDatasources": ["docker"], "matchUpdateTypes": ["major"], "labels": ["breaking-change"], "schedule": ["* 20-23 * * 5"], "automerge": false }, { "matchDatasources": ["helm"], "groupName": "Helm chart updates", "schedule": ["* 2-6 * * 0"], "minimumReleaseAge": "14 days" }, { "matchPackageNames": ["**prometheus**", "**grafana**", "**loki**"], "groupName": "Observability stack", "schedule": ["* 18-23 * * 3"] } ]}The strategy:
- Docker images (minor/patch): group them, auto-merge weekly on Monday night. These are usually low-risk.
- Docker images (major): separate group, flagged “breaking-change”, no auto-merge. You need to read the changelog.
- Helm charts: stricter schedule (Sunday early morning), longer minimum age (14 days). Charts are more likely to carry breaking changes.
- Observability stack: prometheus, grafana, loki batched onto one day.
Two things in that config are easy to get wrong.
Glob your package names. matchPackageNames takes exact strings or glob patterns, and for the docker datasource the package name is the full image path: grafana/grafana, docker.io/prom/prometheus, grafana/loki. A bare "grafana" matches the Helm chart called grafana and misses every image. The **grafana** glob catches both.
Give the schedule room. A schedule doesn’t trigger a Renovate run. It only permits one, and Renovate skips the repo entirely outside the window. On the free hosted plan your repo is scanned every 4 hours, so a two-hour window like * 22-23 * * 1 can be missed for weeks. Renovate’s own docs recommend at least 3 to 4 hours. That’s why the rules above use 20-23 rather than 22-23.
minimumReleaseAge means “don’t open a PR for a version that’s been out for less than X days.” It filters early-adopter pain and gives maintainers time to yank a bad release. Use it for that, not as a general slowdown: for a package that releases too often, a narrower schedule is the right tool.
ArgoCD and Flux Compatibility
Both ArgoCD and Flux are GitOps operators. Renovate doesn’t care which one you use, it just commits to your Git repo, and your operator syncs the changes. Here’s the shape of it:
With ArgoCD:
- Renovate commits the YAML change to your repo.
- ArgoCD detects the commit, syncs the new manifest, applies it.
- If the sync fails, ArgoCD shows an error, and you investigate.
With Flux:
- Renovate commits the change.
- Flux reconciles and applies it.
- Flux logs show the result (check
flux logsor Prometheus metrics).
In both cases, Renovate just needs write access to your repo. No special integration needed. It’s blissfully simple.
But here’s a gotcha: if you have a commit hook or branch protection rule that runs tests, Renovate respects it. Your CI will run against Renovate’s PR, and if the tests fail, the PR stays open until you fix it. This is actually good, you catch bad chart versions before they’re deployed, but it means you need solid test coverage on your manifests. At minimum, a helm lint and a kube-score check.
Common Mistakes
Mistake 1: Not pinning versions in your base Helm charts.
If your Chart.yaml says version: "*", Renovate will open a PR for every new version, even breaking ones. Use semver constraints: "3.x" for minor/patch safety, ">=3.0.0,<4.0.0" for explicit ranges.
Mistake 2: Putting pinDigests in the wrong place.
If you’re pinning image digests (good practice), set pinDigests: true at the top level of renovate.json or inside a packageRules entry. It is not a customManagers field, and nesting it there fails validation. Your regex still has to capture currentDigest alongside currentValue, or you’ll end up with a fresh tag pointing at a stale digest.
Mistake 3: Opening too many PRs at once. If you don’t schedule and group, Renovate becomes noise, and you’ll disable it. Start with one grouping strategy (e.g., “all Docker images weekly”), prove it works for a month, then add complexity.
Mistake 4: Not configuring vulnerabilityAlerts.
Security patches should never be grouped with regular updates. They should be fast-tracked, labeled, and reviewed immediately. Make them impossible to ignore.
Mistake 5: Forgetting branch protection interacts with automerge. If your repo requires branch protection (reviews, status checks), Renovate’s PRs will respect that. If you trust Renovate’s patch updates, set up auto-merge, but only for patch versions. Major versions still need human eyes.
The Endgame
After a few months of Renovate doing its thing, you’ll have:
- Zero manual version bumps: No more 2 AM “did I update all the things?” anxiety.
- Regular, small, predictable PRs: One or two a week, grouped intelligently, easy to review.
- Audit trail: Every version bump is a commit. You can blame, revert, or investigate easily.
- Early warning system:
vulnerabilityAlertsignores your schedule, so a security patch gets a PR on the next scan instead of waiting for Monday night’s window.
It’s not magic. It’s just the boring work automated away so you can focus on the stuff that actually matters, testing, incident response, and building the next thing.
Set it and forget it. Your future self will thank you.
Common Questions
Does Dependabot support Helm charts now?
Yes. Dependabot ships a helm package ecosystem that reads Chart.yaml dependencies, Chart.lock, and repository/tag image pairs in values.yaml. Its docker ecosystem updates image tags in Kubernetes manifests. Renovate still wins on custom managers, grouping, and minimumReleaseAge. Basic Helm awareness stopped being the difference.
Is the free Mend Renovate plan enough for a home lab?
Yes. Mend Renovate Community Cloud covers unlimited public and private repos. The caps are throughput: one concurrent job per org, active repos scanned every 4 hours, 1 vCPU and 3 GB RAM per job, and a 30 minute timeout. A single GitOps repo scans in under a minute.
Do I need a custom manager for images in a Helm values file?
Usually no. The built-in helm-values manager already parses the image.repository plus image.tag shape. It only matches files literally named values.yaml, so if yours is traefik-values.yaml, widen helm-values.managerFilePatterns instead of writing and maintaining a regex.
Why is Renovate ignoring my schedule?
A schedule permits runs. It never triggers them. If the hosted plan scans your repo every 4 hours and your window is two hours wide, most windows get missed entirely. Renovate’s docs recommend windows of at least 3 to 4 hours. Widen the window before you debug anything else.
Can I safely auto-merge Renovate PRs in a GitOps repo?
Yes, for patch updates with branch protection turned on. Set automerge: true and automergeType: "pr", then require passing status checks so a failing helm lint or kube-score run blocks the merge. Add minimumReleaseAge so yanked releases never reach you. Never auto-merge majors.