Why I chose Helm over Kustomize
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.
- The moment a manifest needs a conditional or a loop, Kustomize can't do it — it has no
- 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 revertand 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.jsonto 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.
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-valueswipes 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.
