Search across all documentation pages
Both approaches solve the same problem: you have a Deployment, Service, and ConfigMap that are nearly identical across dev, staging, and production, and you do not want three hand-maintained copies.
The difference is where the variation lives and how it is applied.
Templating (Helm's model) generates final YAML from parameterized text. You write a template with placeholders, supply a values.yaml, and a render engine produces the manifests.
Overlaying (Kustomize's model) starts from real, valid YAML - the base - and applies declarative patches on top to produce each environment's output.
A useful mental model: templating is a function render(template, values) -> yaml, while overlaying is a fold patch(base, [patchA, patchB]) -> yaml.
Templating can express things overlays cannot easily express, such as generating a variable number of objects from a list.
Overlaying can never produce invalid intermediate YAML, because every layer is itself parseable Kubernetes YAML.
Helm uses Go's text/template engine plus the Sprig function library. A chart directory holds templates/, a values.yaml, and Chart.yaml.
Placeholders look like {{ .Values.replicaCount }}, and control flow like {{- if .Values.ingress.enabled }} lets you include or omit whole objects.
At install time Helm renders the templates, merges the result, and sends the objects to the API server as a tracked release.
# templates/deployment.yaml (Helm)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "app.fullname" . }}
spec:
replicas: {{ .Values.replicaCount }}
template:
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"Kustomize walks a kustomization.yaml, loads the referenced resources, then applies transformers and patches. There is no text substitution and no templating language.
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
replicas:
- name: app
count: 5
images:
- name: app
newTag: 1.8.3The base contains complete manifests; the overlay only records the delta for that environment.
Kustomize is built into kubectl (kubectl apply -k), and Helm is a separate binary that also talks to the cluster and stores release state as Secrets.
The two can interact: Kustomize can consume Helm-rendered output through its helmCharts field, letting you post-process a third-party chart with overlays.
Templating shines for redistributable software. If you publish a database or an ingress controller, a chart with well-documented values is the expected delivery format.
Overlaying shines for your own application manifests in a GitOps repo, where reviewers want to see exactly what changes between staging and production.
Consider readability of diffs during review. An overlay pull request shows a two-line replica change; a Helm change may show only a values edit whose rendered impact is not visible without helm template.
Consider the failure mode of each. A template bug can emit malformed YAML that only fails at apply time; an overlay bug usually fails at build time because Kustomize validates structure.
Many teams run a hybrid: package with Helm for distribution, then feed the rendered chart into Kustomize for site-specific tweaks that the chart author never anticipated.
For GitOps, both render deterministically in CI. Argo CD and Flux natively support Helm charts and Kustomize directories, so either can be the source of truth.
Whichever you choose, keep the render step in CI. Running helm template or kustomize build and committing or diffing the output catches surprises before they reach the cluster.
"Kustomize cannot do parameterization." It can vary images, replicas, names, labels, and arbitrary fields via patches; it just cannot inject free-form variables into arbitrary text.
"Helm requires Tiller and cluster-side components." That was Helm 2. Helm 3 is client-only and stores release state as Secrets in the target namespace.
"You must choose one tool forever." They compose. Kustomize can post-render Helm output, and Helm 3 supports post-renderers that call Kustomize.
"Templating is always more powerful, so always use Helm." Power is not free; heavy template logic is a common source of unreadable, hard-to-review charts.
"Overlays only patch fields." Overlays can also add resources, set common labels and annotations, generate ConfigMaps, and prefix names across every object.
Which produces valid Kubernetes YAML at every step? Kustomize, because each layer is real YAML. Helm intermediate templates are text and only become valid after rendering.
Can I see the final output before applying? Yes. Use helm template ./chart -f values.yaml or kustomize build overlays/production.
Does Kustomize have variables? It has replacements and generators, but no general-purpose variable substitution like a templating language.
Which is better for a public open-source component? Helm, since charts are the conventional distribution format with discoverable values.
Which is better for internal app promotion across environments? Kustomize overlays usually give smaller, clearer per-environment diffs.
Can I use both together? Yes. A common pattern is Helm for packaging and Kustomize for last-mile, cluster-specific patches.
Stack versions: This page was written for Kubernetes 1.36.2, Docker Engine 29.6.1 (BuildKit default), containerd (CRI runtime on nodes), Helm 3, Compose v2, Argo CD (latest - verify at build), and Gateway API (GA - verify controller support at build).
Reviewed by Chris St. John·Last updated Jul 16, 2026