Automating Helm Chart Updates in My Home Lab with Renovate, GitLab CI, and ArgoCD

On this page Table of Contents

Keeping Helm chart dependencies up to date in a home lab can be tedious. You deploy a chart, pin a version, and months later you’re running something outdated without even realizing it. In this post, I’ll walk through how I set up Renovate to automatically detect outdated Helm charts in my GitOps repository and open merge requests on my self-hosted GitLab instance.

The Setup

My home lab runs Kubernetes with ArgoCD managing deployments via a GitOps approach. The repository structure follows an app-of-apps pattern using ArgoCD ApplicationSets:

argo/
├── apps/
│   ├── alloy/
│   │   ├── Chart.yaml
│   │   └── values.yaml
│   ├── beyla/
│   ├── fluentbit/
│   ├── gitlab-agent/
│   ├── gitlab-runner/
│   ├── kube-state-metrics/
│   ├── kubernetes-dashboard/
│   ├── metrics-server/
│   └── openebs/
├── applicationset.yaml
└── renovate.json

Each app directory is a wrapper Helm chart – a minimal Chart.yaml that declares a dependency on an upstream chart from an external repository. For example, here’s what apps/kube-state-metrics/Chart.yaml looks like:

apiVersion: v2
name: kube-state-metrics
description: A Helm chart for Kubernetes
type: application
version: 0.1.0
appVersion: "2.6.0"
dependencies:
- name: kube-state-metrics
  version: 4.21.0
  repository: https://prometheus-community.github.io/helm-charts

The values.yaml file alongside it contains any configuration overrides for my environment.

An ArgoCD ApplicationSet watches the apps/ directory and automatically creates an Application for each subdirectory:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: apps
  namespace: argocd
spec:
  generators:
  - git:
      repoURL: https://git.aganet.gr/georgios/argo.git
      revision: master
      directories:
      - path: apps/*
  template:
    metadata:
      name: '{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://git.aganet.gr/georgios/argo.git
        targetRevision: master
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: '{{path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        retry:
          limit: 3
          backoff:
            duration: 30s
            maxDuration: 3m
            factor: 2
        syncOptions:
          - CreateNamespace=true
          - ServerSideApply=true

This means any change merged to master is automatically synced to the cluster. The missing piece was: who bumps the versions in Chart.yaml?

The Solution: Renovate

Renovate is a universal dependency update tool that supports a wide range of package managers and languages – from npm and pip to Docker, Terraform, and Helm. In our case, it natively understands Helm Chart.yaml files, queries the upstream repositories for newer versions, and opens merge requests with the version bumps.

Step 1: Add the Renovate Configuration

I added a renovate.json to the repository root:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "includePaths": ["apps/**"],
  "prHourlyLimit": 20
}

Key settings:

  • includePaths: Tells Renovate to only scan the apps/ directory. I have other directories (apps2/… etc, standalone charts) that I don’t want Renovate to touch.
  • prHourlyLimit: The default is 2 MRs per hour, which is conservative. I bumped it to 20 so all updates are created in a single run.

Step 2: Create the GitLab CI Pipeline

Since I’m running a self-hosted GitLab, I can’t use the Renovate GitHub App. Instead, I run Renovate as a CI job triggered on a schedule.

My GitLab runner uses the shell executor (not Docker), so I use npx to run Renovate directly:

renovate:
  stage: build
  tags:
    - shell-host
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
  variables:
    RENOVATE_PLATFORM: gitlab
    RENOVATE_ENDPOINT: ${CI_API_V4_URL}
    RENOVATE_TOKEN: ${RENOVATE_TOKEN}
    RENOVATE_REPOSITORIES: ${CI_PROJECT_PATH}
    LOG_LEVEL: debug
  script:
    - npx renovate

A few notes:

  • The job only runs on scheduled pipelines, not on every push.
  • CI_API_V4_URL and CI_PROJECT_PATH are predefined GitLab CI variables – no hardcoding needed.
  • RENOVATE_TOKEN is a Personal Access Token with api scope, stored as a CI/CD variable in the project settings.
  • I keep LOG_LEVEL: debug enabled since it’s very useful for troubleshooting.

Step 3: Create a Pipeline Schedule

In GitLab, go to Build > Pipeline schedules and create a new schedule. I run mine daily, but weekly works fine for a home lab.

Step 4: Create the Personal Access Token

This was the trickiest part. To create the token:

  1. Go to your GitLab profile Edit profile > Access Tokens
  2. Create a new token with the api scope
  3. Add it as a CI/CD variable named RENOVATE_TOKEN in your project settings

The Results

On the first run, Renovate scanned all 9 charts in apps/ and detected updates for almost everything:

Chart Current Available
alloy 0.11.0 0.12.6
beyla 1.7.2 1.11.0
fluent-bit 0.50.0 0.55.0
gitlab-agent 2.13.0 2.23.0
gitlab-runner 0.75.0 0.85.0
kube-state-metrics 4.21.0 4.32.0
metrics-server 3.12.2 3.13.0
openebs 4.2.0 4.4.0

For charts with major version bumps (like alloy and kube-state-metrics), Renovate creates separate MRs for the minor and major updates, giving you the choice to stay on your current major version or upgrade.

Renovate merge requests in GitLab
Renovate merge requests in GitLab

Surprise Discovery: kubernetes-dashboard is Dead

Renovate also surfaced something I hadn’t noticed – the kubernetes-dashboard Helm repository is gone:

GET https://kubernetes.github.io/dashboard/index.yaml = 404
Failed to look up helm package kubernetes-dashboard

The kubernetes-dashboard project was archived in January 2026. The Helm repo no longer serves index.yaml, so ArgoCD can’t sync it either. The recommended replacement is Headlamp, now under Kubernetes sig-ui.

This is one of the underrated benefits of Renovate – it doesn’t just find updates, it also alerts you when a dependency is no longer available.

The Full GitOps Loop

With everything in place, the workflow is:

  1. Renovate runs on schedule via GitLab CI
  2. It detects outdated Helm chart versions in apps/
  3. It opens merge requests with the version bumps
  4. I review and merge the MR
  5. ArgoCD detects the change in the git repository
  6. ArgoCD syncs the updated chart to the cluster

No manual version checking. No forgetting to update. Just merge requests waiting for my review.

Tips and Gotchas

  • Hourly PR limit: Renovate defaults to 2 MRs per hour. If you have many charts, bump prHourlyLimit or you’ll be waiting multiple runs for all MRs to appear.
  • Debug logging: Keep LOG_LEVEL: debug enabled, at least initially. The output is verbose but invaluable when something isn’t working as expected.
  • ServerSideApply: If your Helm charts include large CRDs, add ServerSideApply=true to your ArgoCD sync options. This avoids the metadata.annotations too long error that can happen with standard apply.

Conclusion

Setting up Renovate for a home lab GitOps repository took about an hour, and most of that was debugging the authentication token. The actual configuration is just two files: a renovate.json and a CI job in .gitlab-ci.yml. Now my Helm charts stay current with zero manual effort – exactly the kind of automation a home lab should have.

References

Comments