Helm vs Kustomize for Kubernetes Config
Helm templates and packages Kubernetes manifests with a templating language; Kustomize patches plain YAML declaratively, with no templates at all.
Helm and Kustomize solve the same underlying problem — managing Kubernetes YAML across multiple environments without duplicating entire manifest files — but they take opposite approaches. Helm treats manifests as templates filled in with variables and packages them into versioned, shareable units called charts. Kustomize treats manifests as plain, valid YAML and layers declarative patches on top, with no templating language involved at all.
Helm: templates and packages
A Helm chart is a directory of templated YAML files plus a values.yaml file supplying the variables that fill them in. Deploying a chart means rendering the templates with a given set of values and applying the result:
# templates/deployment.yaml
spec:
replicas: {{ .Values.replicaCount }}
containers:
- image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
Different environments — staging, production, a customer’s own cluster — get different values.yaml overrides, but share the same underlying templates. Charts can be versioned, published to repositories, and installed with a single helm install command, which is why Helm became the default way to distribute complex, multi-resource applications (an ingress controller, a database operator, a full observability stack) as a single reusable package. Helm also tracks release history, so helm rollback can revert a release to a previous rendered state, and it manages the full lifecycle of ConfigMaps and Secrets or CRDs that a chart defines, alongside the core Pods, Deployments, and Services it deploys.
The cost is the templating language itself — Go templates embedded in YAML, which are not YAML until rendered. Debugging a broken chart often means running helm template to see the rendered output before you can tell whether the problem is in the template logic or the underlying manifest.
Kustomize: patches on plain YAML
Kustomize takes a different position: manifests should stay valid, plain YAML, with no template syntax and no variables. Instead of filling in placeholders, you write a base set of manifests and then define overlays that patch specific fields for each environment:
# overlays/production/kustomization.yaml
resources:
- ../../base
patches:
- target:
kind: Deployment
name: web
patch: |-
- op: replace
path: /spec/replicas
value: 5
Because the base manifests are always valid YAML on their own, they can be read, edited, and applied directly with kubectl apply -k without any rendering step. Kustomize ships built into kubectl itself, so no separate tool installation is required to use the basic overlay pattern. The tradeoff is that Kustomize has no packaging or distribution model comparable to Helm charts — there’s no equivalent of helm install some-chart pulling in an entire pre-built application with one command, and no built-in release history or rollback tracking the way Helm maintains it.
Comparing the two directly
| Helm | Kustomize | |
|---|---|---|
| Base format | Go templates, not valid YAML until rendered | Plain, valid YAML throughout |
| Environment differences | values.yaml overrides | Overlay patches (JSON patch or strategic merge) |
| Packaging/distribution | Versioned charts, chart repositories | No native packaging — overlays reference base directories |
| Built into kubectl | No — separate CLI | Yes (kubectl apply -k) |
| Release history/rollback | Built in (helm rollback) | Not built in — relies on external tooling or GitOps history |
| Best fit | Distributing reusable, third-party, or complex multi-resource apps | Managing environment-specific variants of your own manifests |
They aren’t mutually exclusive
A common pattern combines both: use Helm to install third-party components that are only distributed as charts — an ingress controller or a monitoring stack, for instance — while using Kustomize (or helm template piped into a Kustomize overlay) to manage your own application manifests, where plain YAML and patches are easier to review in a pull request than templated files. Kustomize can even post-process the output of helm template, letting teams get Helm’s packaging for dependencies without adopting Go templates for their own manifests.
Both tools are commonly wired into a GitOps workflow, where a reconciliation controller watches a Git repository and applies whatever the rendered manifests describe — the choice of Helm or Kustomize mostly affects how that repository is structured, not whether GitOps itself works. Compared to a general-purpose infrastructure tool like Terraform, both Helm and Kustomize operate specifically on Kubernetes manifests rather than cloud infrastructure broadly, which is why they’re often used alongside Terraform rather than instead of it — Terraform provisions the cluster, Helm or Kustomize configures what runs inside it.
Picking one
If you’re distributing an application for other people to install into clusters you don’t control, or you need versioned releases with rollback, Helm’s packaging model is hard to replace. If you’re managing your own manifests across a handful of environments and want to review diffs as plain YAML in a pull request without a templating layer in between, Kustomize’s patch-based approach tends to be easier to reason about and easier to review. Many teams end up using both, for different halves of the same cluster.
The takeaway
Helm packages templated manifests into versioned, distributable charts with built-in release tracking; Kustomize patches plain, valid YAML through layered overlays with no templating language at all. Helm wins for distributing reusable applications and needing rollback history; Kustomize wins for keeping your own manifests reviewable as ordinary YAML. Neither replaces the other outright, and plenty of clusters run both side by side.
Tagged
Keep reading
Chisato · · 4 min read Kubernetes Init Containers Explained
Init containers run to completion before a pod's main containers start, making them the standard way to handle setup steps and startup ordering.
Chisato · · 4 min read Kubernetes Service Types: ClusterIP vs NodePort vs LoadBalancer
Kubernetes offers four Service types for exposing pods on the network. How ClusterIP, NodePort, LoadBalancer, and ExternalName each route traffic.
The Lycoris Team · · 5 min read What Are Kubernetes CRDs? Custom Resources Explained
A Kubernetes CRD (CustomResourceDefinition) extends the API with new resource types, letting the cluster manage custom objects like native ones.