Helm vs Kustomize for Kubernetes: When Each One Wins
Both can deploy your Kubernetes app. The choice is about templating philosophy, upgrade ergonomics, and how much config drift you tolerate.
Once you’re past kubectl apply -f for everything, the next decision is how to package and deploy your Kubernetes manifests. Two serious options dominate: Helm (templating + package management) and Kustomize (overlays without templating). Both work. The choice is dominated by how your team thinks about configuration and how you handle the “20 environments, slightly different” problem.
We’ve shipped both for clients across hospital platforms, banking SaaS, and logistics workloads. Here’s how we actually pick.
The thirty-second framing#
- Helm is “Kubernetes apps are packages.” A chart bundles templated YAML, values files override defaults, and
helm installdeploys the whole thing as a release. The package manager mental model — apt for Kubernetes. - Kustomize is “Kubernetes apps are layered YAML.” A base directory holds the canonical manifests; overlay directories patch them per environment. No templating, just strategic merge patches and JSON patches.
Both produce the same end result (Kubernetes manifests applied to a cluster). The mental models differ sharply.
What’s actually different#
| Dimension | Helm | Kustomize |
|---|---|---|
| Template engine | Go templates ({{ .Values.x }}) | None — patch-based overlays |
| Built-in to kubectl | No (separate binary) | Yes (kubectl apply -k) |
| Lifecycle management | Yes (releases, revisions, rollbacks) | No (just produces YAML) |
| Package distribution | Helm registries (OCI, ChartMuseum) | Git repos / git refs |
| Values shape | YAML values file → templates | Base YAML → patches |
| Conditional logic | Powerful (if/else, range, with) | None |
| Learning curve | Steeper (templates + chart structure) | Gentler (just YAML patches) |
| Debugging | Hard (templates → manifests → cluster) | Easier (the YAML you write is the YAML applied) |
| Drift management | Helm tracks state via secrets in the cluster | None (depends on your GitOps tool) |
| Multi-environment | Multiple values files per env | Overlays per env |
Where Helm wins#
Third-party application distribution. If you’re installing Postgres, Redis, ArgoCD, cert-manager, or any of the thousand other things people deploy to Kubernetes, Helm is the lingua franca. The maintained charts are how those tools ship. Even if you don’t love Helm, you’ll use it for these.
Release semantics. helm upgrade knows what version is currently installed, computes the diff, and tracks the history. helm rollback reverses to a previous release. For “fast forward, fast back” deployment workflows, this is real value.
Powerful templating. Conditionals ({{ if .Values.tls.enabled }}), loops ({{ range .Values.envs }}), helpers ({{ template "common.labels" . }}). For applications with significantly different shapes across environments, templating reduces duplication.
Package distribution. OCI registries (the same registries you push container images to) now support Helm charts. Versioned, signed, distributable.
Where Helm hurts: templates can become unreadable. A 200-line deployment.yaml template with 40 {{ if }} blocks is a debugging nightmare. The mental model “imagine what this template would render with these values” is harder than “look at the final YAML.”
Where Kustomize wins#
The output is the input. What you see in your overlay directory is what gets applied (after merging with the base). No template-to-YAML mental translation. For debugging, this matters a lot.
Native in kubectl. No extra binary. kubectl apply -k overlays/production is one command.
Strategic merge patches feel right. Kustomize lets you patch a specific field in a specific resource without re-templating the whole thing. For “the staging environment is identical to base except CPU limits are different,” that’s a 5-line patch, not a 200-line template.
Generators. ConfigMap and Secret generators that hash inputs into the resource name — so changing a ConfigMap’s content forces a pod restart. Subtle but valuable.
Components (Kustomize 4+). Reusable patch bundles you can pull into multiple overlays. Closes some of the templating expressiveness gap.
Where Kustomize hurts: no conditionals. If your environments differ structurally (production has 3 replicas + an HPA + a PDB, dev has 1 replica + nothing), you’re either duplicating YAML or wrestling components. No lifecycle management — kustomize just emits YAML; tracking what’s deployed where is on your GitOps tool.
The pattern most teams converge on#
After enough years, most teams we work with end up with the same shape:
- Helm for third-party stuff. Postgres, Redis, observability tooling, ingress controllers, cert-manager, etc. — install via Helm because that’s how they ship.
- Kustomize for in-house apps. Your own application’s manifests, with overlays per environment. The YAML you write is the YAML applied.
- Argo CD or Flux to glue it together. GitOps tool understands both. Argo CD’s
ApplicationandApplicationSetresources, Flux’sHelmReleaseandKustomizationresources.
This hybrid is more pragmatic than picking one. Helm earns its keep for distribution; Kustomize earns its keep for in-house clarity.
(See our GitOps piece for the orchestration layer.)
When you go full Helm#
For an in-house application, going all-Helm makes sense when:
- The application has genuinely complex conditional logic (e.g., “deploy with auth-proxy IF the env is non-prod ELSE without”)
- You’re distributing the application to third parties (other teams, customers, partners) and they need a versioned package
- Your team is comfortable with Go templates and chart authoring
If you go this route, invest in: a _helpers.tpl with shared labels/selectors, helm lint in CI, helm template | kubeval to validate output, and unit tests via helm-unittest.
When you go full Kustomize#
For an in-house application, going all-Kustomize makes sense when:
- Environments differ in small, well-defined ways (replicas, resources, hostnames)
- Your team values “see the YAML, change the YAML”
- You’re not distributing the app externally
The bear trap to avoid: deep overlay hierarchies. base/ → env-overlays/ → region-overlays/ → cluster-overlays/. Each layer makes debugging harder. Keep it 2 deep at most.
The third option: cdk8s, Pulumi, jsonnet, etc.#
A few teams reach for code-based alternatives — cdk8s (TypeScript/Python), Pulumi-K8s, jsonnet. These are excellent for complex orchestration and for sharing logic between application code and infrastructure code.
We’ve shipped Pulumi for K8s on a couple of projects where the deployment logic was genuinely complex (multi-tenant cluster shaping per customer). For most projects, the additional dependency isn’t worth it.
Patterns that matter more than the tool choice#
A few things matter more than Helm vs Kustomize for whether your Kubernetes deploys are reliable:
Atomic deploys. If a manifest application partially succeeds, you have a hybrid state. Use ArgoCD’s sync waves or Helm’s --atomic flag.
Resource limits on everything. Pods without requests/limits are the classic 3am page. Bake them into your base manifests; require them at admission via Kyverno or OPA Gatekeeper.
Liveness vs readiness probes are not the same. Liveness restarts the pod; readiness pulls it from the load balancer. Get them wrong and you get either crash loops or traffic to broken pods.
Image pinning. Never deploy :latest. Pin to a specific tag or, better, a SHA digest. :latest is how a “redeploy” silently installs a new version.
Secrets out of git. Whether Sealed Secrets, SOPS, External Secrets Operator, or just symlinked Vault — never plain secrets in your repo, even in private repos.
What we deploy by default#
For a new Kubernetes platform build (the kind we do for hospitals, banks, and SaaS clients):
- Helm for the platform layer: ingress-nginx, cert-manager, ArgoCD, Postgres operator, observability tools.
- Kustomize for application manifests: base/ + overlays per environment.
- ArgoCD as the GitOps deployer, reconciling Helm releases and Kustomize bases.
- External Secrets Operator for secret management.
- Kyverno policies for admission control (resource limits, no
:latest, etc.).
For teams that strongly prefer all-Helm or all-Kustomize, we adapt. Both work; consistency matters more than which.
The pattern of patterns#
Helm and Kustomize aren’t competing — they’re complementary tools that overlap awkwardly in the middle. The mistake is treating them as substitutes. The teams that ship Kubernetes reliably use both for what each is good at: Helm for distribution, Kustomize for in-house clarity.
The tool isn’t what makes Kubernetes deploys reliable. The discipline (pinned images, atomic deploys, resource limits, secret management, GitOps) is. The tool just helps you express that discipline.
The packaging tool is the easy decision. The hard ones are the platform shape. If you’re building a Kubernetes platform and want a sanity check, our DevOps and CI/CD service has shipped both Helm and Kustomize in production. Tell us about the shape.