The debate is usually framed as a features comparison, which is why it never resolves. Both tools can produce the same YAML. Both are mature. Both are used at scale by people who are not wrong.
The useful framing is narrower: Helm templates YAML before it exists, Kustomize patches YAML that already does. Everything below follows from that, including the cases where the answer is obvious.
What is the actual mechanical difference?
Helm is a templating engine plus a package manager. You write Go templates with placeholders, supply a values.yaml, and Helm renders manifests. The chart is a distributable artefact with a version, installed as a named release the tool tracks.
# templates/deployment.yaml
spec:
replicas: {{ .Values.replicaCount }}
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
Kustomize takes valid manifests and layers changes on top. No placeholders, no template language. A base holds real YAML that would apply as-is, and overlays patch it.
# overlays/production/kustomization.yaml
resources:
- ../../base
patches:
- path: replicas.yaml
images:
- name: myapp
newTag: v2.4.0
The consequence people feel first: a Kustomize base is readable on its own. You can open it, understand it, and paste it into a cluster. A Helm template is not valid YAML until rendered, so reading it means running helm template or reading Go template syntax in your head.
The consequence people feel later: Helm can express things Kustomize cannot. Conditionals, loops, computed values, optional blocks. If a chart needs to support both an Ingress and a Route depending on the platform, templating does that. Patching does not, cleanly.
Which one should you use for third-party software?
Helm, and this one is not close.
Nearly every vendor and open source project ships a Helm chart. Prometheus, cert-manager, ingress controllers, databases, operators. Using Kustomize means either finding raw manifests that may not exist, or rendering the chart yourself and vendoring the output.
More importantly, that chart encodes upgrade knowledge you do not have. Which fields moved between versions, what needs recreating, what order things must happen in. Recreating that as overlays means owning it.
Use the chart. Do not fork it. If you need changes the chart does not expose, patch the rendered output rather than maintaining a fork, which is what post-rendering exists for.
Which one for the applications you write yourself?
Kustomize, usually, and the reason is not technical.
Your own services are near-identical to each other and differ across environments in a small, predictable set of ways: replica count, resource limits, image tag, a few environment variables, an ingress host. That is exactly the shape patching handles well and templating over-serves.
Templating your own app means every application team now writes Go templates to change a replica count. That is a language between a developer and their configuration, and they pay that cost every time they touch it. In exchange you get flexibility they mostly do not use.
The exception: if you ship your application to other people who run it themselves, you are a vendor, and vendors ship charts. Same reasoning as above, from the other side.
Can you use both?
Yes, and it is the most common mature setup rather than a compromise.
Helm for other people's software. Kustomize for yours. Two tools, one clear rule for which is which, no argument per repository.
They also compose directly. Helm 3 supports a post-renderer, so you can render a chart and let Kustomize patch the output:
helm install app ./chart --post-renderer ./kustomize-wrapper.sh
Or the other direction, rendering a chart into manifests Kustomize treats as a base. Both are legitimate and both keep you from forking someone else's chart to change one label.
Does either one manage state?
This is the difference that matters at deploy time and gets discussed least.
Helm tracks releases. It records what it installed as a versioned release in the cluster, which gives you helm rollback and helm uninstall that actually removes what it created.
Kustomize does not. kubectl apply -k applies YAML. It has no memory of what it applied before, so a resource you removed from the overlay stays in the cluster unless you prune it deliberately. Pruning is possible and it is sharp: it needs correct labelling, and getting it wrong deletes things.
For a platform team this usually resolves itself, because a GitOps controller supplies the state tracking either way. Argo CD and Flux both track what they applied and both understand Kustomize and Helm. If you are running one, Helm's release tracking is largely redundant. If you are applying from CI with kubectl, it is a real advantage.
What breaks in each one at scale?
Helm's failure mode is unreadable charts. Nested conditionals, values files that inherit from values files, subcharts whose defaults you have to trace through three levels. Debugging becomes helm template piped to a pager, comparing rendered output to what you expected. A chart that has grown for three years is frequently the least approachable code a platform team owns.
Kustomize's failure mode is overlay sprawl. Overlays inheriting from overlays inheriting from a base, each adding a patch, and nothing at any single level tells you what the final object looks like. kubectl kustomize is your only way to know, and strategic merge patches on lists behave in ways that surprise people every time.
Both failure modes are the same shape: the thing you read is not the thing that applies. Helm gets there through templating, Kustomize through layering. Whichever you pick, the discipline that saves you is keeping the depth shallow and rendering the output in CI so a human can see the diff, the same way a Terraform plan belongs in the review.
How do you decide?
Four questions, in order.
- Is it someone else's software? Helm. Done.
- Do you distribute your app to external users? Helm. You are a vendor.
- Do application teams edit their own manifests? Kustomize. Do not put a template language between a developer and their replica count.
- Do you need conditionals or loops for genuinely different topologies? Helm, or generate the YAML from a real programming language and stop pretending YAML is the right layer.
If none of those decide it, pick Kustomize for your own workloads. It is in kubectl, there is nothing extra to install, and a base you can read is worth more than flexibility you have not needed yet.
The short version
Helm templates, Kustomize patches. Use Helm for software other people wrote and for anything you distribute. Use Kustomize for your own services, where the differences between environments are small and predictable.
Running both is not indecision. It is the setup most teams arrive at, and having one sentence that says which is which is worth more than winning the argument.
DevLift generates Kubernetes manifests and deployment configuration from a service definition, and opens a pull request so the rendered output is reviewed before it reaches a cluster. Book a walkthrough.