Skip to main content

Why I chose Helm over Kustomize

· 4 min read
Charles Wang
DevOps / Backend Engineer

Two weeks ago I gave myself a task: write a Helm chart from scratch and really get Helm.
After finishing the exercise, I also did a tool-selection exercise alongside it,
thinking about why I'd choose Helm when Kustomize is simpler.
I asked myself "what capability does this situation actually need," letting the requirements pick the tool, rather than letting features pick me.

What I need: let the requirements pick the tool, not the features pick me

I broke the requirements into four questions to decide between Kustomize and Helm:

  • Do I need logic?
    • The moment a manifest needs a conditional or a loop, Kustomize can't do it — it has no if / range.
  • Do I need a lifecycle?
    • install / upgrade / rollback, with release state stored in a namespace Secret in the cluster — this is the core of Helm.
    • Kustomize has no concept of a release; rolling back means git revert and re-apply.
  • Do I need dependencies?
    • My existing redis and postgresql are both charts; integrating them as subcharts is directly supported by Helm.
  • Do I need hooks?
    • Running a Job pre-install is something Helm has hooks for; Kustomize doesn't.

If my need were just "apply the same manifest to three environments, tweaking replicas and the image," I'd pick Kustomize — that's where it's at its cleanest.

The exercise: the chart I wrote myself

I practiced by writing a chart that actually runs. The point isn't the few helm create commands (anyone can scaffold...).
The real learning is reshaping that "does everything" default chart into your own. I added:

  • a ConfigMap
  • a pre-install hook Job
  • a values.schema.json to block bad input.

helm create gives you an all-capable skeleton that isn't yours; turning it into your own — the whole process is really about deciding what to throw away, what to add, and what to keep.

Repo: https://github.com/shoudevops/helm-practice

Removing a feature ≠ deleting a file: the tentacles of autoscaling

The most eye-opening practice came from "deleting a feature."
I wanted to remove autoscaling; my instinct was to just delete hpa.yaml.
I thought I was done, but rendering blew up — that line in deployment.yaml, {{- if not .Values.autoscaling.enabled }}, was still referencing a value I thought no longer existed, straight to a nil pointer.
It turned out this feature's tentacles weren't only in hpa.yaml: values.yaml had its settings, NOTES.txt mentioned it, and the deployment's replicas logic was hooked into it too.
I chased them one by one, and only after I got to that line in the deployment was it truly clean.

What I learned from the process: deleting a feature means chasing every one of its references in values / other templates / NOTES; deleting the file is only step one.
Incidentally, I also stepped on two landmines along the way:

  • never let secrets into git
  • --reset-values wipes out the custom values you've accumulated.

When I'd switch to Kustomize

This post isn't saying Helm is some cure-all.
For third-party apps I'll install with Helm;
but for purely environment-to-environment differences, I'll use Kustomize overlays — or even render with helm template first and finish with a Kustomize patch.

The criterion is still the same one:

  • once I need conditionals or rollback, Kustomize is out; if it's just changing a few values, I don't need Helm.

Wrap-up

Back to the question at the very start: why not Kustomize?
Because tool-selection skill was never about reciting which tool has which features — it's about honestly asking yourself "what do I need" first, then letting the requirements pick the tool.
Next I want to pick up secret management — SOPS or External Secrets Operator — which is yet another "let the requirements pick the tool" topic.