Aller au contenu

Why I Moved from Helm to Kustomize — With Real Examples

Sitemap

Mastodon## Towards Dev

Towards Dev

A publication for sharing projects, ideas, codes, and new theories.

In Kubernetes land, Helm is often the go-to tool for templating and deploying applications. I used Helm for years, and while it served me well, I gradually ran into limitations — especially as I scaled into GitOps, multi-env setups, and more opinionated CI/CD pipelines.

Eventually, I migrated much of my infrastructure and app deployments to Kustomize. In this blog, I’ll explain why I made the switch, and give real code comparisons to show how Kustomize simplified configuration management — especially around environment-specific changes.

🌩️ Problem: Helm Gets Complex as You Scale

Helm is powerful, but its templating model can become overly complex when:

  • Managing multiple environments (e.g., dev, staging, prod)
  • Making small changes across multiple values.yaml files
  • Trying to diff or audit rendered manifests
  • Avoiding logic mixed with YAML via Go templates

Let’s look at a simple example: deploying an NGINX app and modifying the replica count and image tag for different environments.

🧪 Helm Example: Overhead in values.yaml

**templates/deployment.yaml**

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.app.name }}
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: {{ .Values.app.name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

**values.yaml** (base)

app:
  name: nginx
replicaCount: 2
image:
  repository: nginx
  tag: "1.25"

**values-prod.yaml**

replicaCount: 4
image:
  tag: "1.26"

To deploy:

helm install nginx ./nginx-chart -f values.yaml -f values-prod.yaml

At first glance, this seems fine. But what happens when:

  • You add new environments?
  • You want GitOps to diff changes?
  • You want overlays but not maintain 5 different values files?
  • Need to add the new variables like {{ .Values.xxxx }} if they don’t already exist—not just in the values.yaml file, but also in the template YAML files?

Imagine you’re not just dealing with a few templated YAML files, but managing thousands of them. All template Yaml files are full of weird **{{ }}**

✅ Kustomize Example: Simple and Layered

**base/deployment.yaml**

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.25

This plain YAML file is easy to read and understand, correct?

**overlays/prod/kustomization.yaml**

resources:
  - ../../base
patchesStrategicMerge:
  - deployment-patch.yaml

**overlays/prod/deployment-patch.yaml**

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 4
  template:
    spec:
      containers:
        - name: nginx
          image: nginx:1.26

To deploy:

kustomize build overlays/prod | kubectl apply -f -

🔄 Making a Change: Helm vs Kustomize

Let’s say we want to increase replicas from 4 to 5 in production.

🛠️ In Helm:

You edit values-prod.yaml:

replicaCount: 5

Then re-run:

helm upgrade nginx ./nginx-chart -f values.yaml -f values-prod.yaml

But:

  • Helm must re-render templates every time.
  • Small changes are hidden inside values.
  • Git diffs aren’t easily readable from the rendered manifests.

🛠️ In Kustomize:

You update just this file:

**overlays/prod/deployment-patch.yaml**

spec:
  replicas: 5

Then re-run:

kustomize build overlays/prod | kubectl apply -f -

✅ Clear.
✅ Declarative.
✅ Easy to track changes in Git.

More real and complext example to compare

Helm chart: https://github.com/grafana/grafana-operator/blob/master/deploy/helm/grafana-operator/templates/deployment.yaml

Kustomize: https://github.com/grafana/grafana-operator/blob/master/deploy/kustomize/base/deployment.yaml

🧠 Lessons Learned

  • Kustomize forces clarity — no hidden logic, no magic defaults.
  • Helm can get messy when managing environments at scale.
  • Kustomize plays better with GitOps tools like ArgoCD, Flux, and GitHub Actions.
  • I still use Helm for third-party apps (like Prometheus or Redis) where community charts save time.

Towards Dev

Towards Dev

Last published 18 hours ago

A publication for sharing projects, ideas, codes, and new theories.

More from Bill WANG and Towards Dev

[

See more recommendations

](https://medium.com/?source=post_page---read_next_recirc--33ea172d5113---------------------------------------)