Kustomize: Kubernetes Configuration Without the Template Sprawl of Helm
Plain YAML, patched and overlaid, instead of a templating language bolted onto YAML

There’s a moment, somewhere around your third environment, when a Helm chart stops being a convenience and starts being a small programming language you didn’t ask to learn. You’re staring at {{- if .Values.ingress.enabled }} nested four levels deep, whitespace-sensitive, debugged by squinting at helm template output. The thing you wanted was “the same manifests as production, but with a different replica count and hostname.” The thing you got was Go templating wrapped around YAML, which is a syntax wrapped around a syntax, and your editor can’t help you with either.
Kustomize takes a different bet. Your manifests stay plain, valid, schema-checkable Kubernetes YAML. To vary them, you overlay — you patch the base. No template language, no {{ }}, no rendering step that turns text into more text and hopes it parses. I came to it grudgingly and stayed happily.
1 The base-and-overlays idea
You write your real manifests once, in a base directory. Then each environment is a thin overlay that references the base and describes only its differences. The mental model is inheritance, not substitution.
myapp/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── staging/
│ └── kustomization.yaml
└── production/
├── kustomization.yaml
└── replicas-patch.yamlThe key insight is that the base is complete and deployable on its own. You can apply the base directly, with no overlay, and get a working application. The overlays don’t fill in blanks — there are no blanks — they amend something that already stands up. That’s the opposite of a Helm chart, where the bare templates are inert until you feed them values. It also means you can review the base in isolation and trust that what you read is what runs.
The base’s kustomization.yaml just lists the resources:
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yamlAnd production overlays its changes on top:
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: prod
namePrefix: prod-
resources:
- ../../base
patches:
- path: replicas-patch.yaml
images:
- name: myapp
newTag: v2.3.1That replicas-patch.yaml is a strategic-merge patch — it looks exactly like the deployment, but with only the fields you want to change. Kustomize merges it into the base. No new syntax to learn; if you can write a Deployment, you can write the patch.
2 It’s built into kubectl
This is the part people miss. You don’t have to install anything. kubectl ships with Kustomize built in:
# Render and inspect what you'd actually apply:
kubectl kustomize overlays/production
# Apply it directly:
kubectl apply -k overlays/productionThe standalone kustomize binary is usually a version or two ahead and worth installing for serious use, but for kicking the tyres, the -k flag is already on your machine.
3 The transformers you’ll actually use
Beyond patches, Kustomize has a handful of built-in transformers that do the boring-but-error-prone rewrites for you. namePrefix/nameSuffix rename every resource so staging and prod can coexist. commonLabels stamps a label across everything, including the selectors, so you don’t desync a Deployment from its Service. images swaps tags without you hunting through YAML. And configMapGenerator builds a ConfigMap from a file or literals, appending a content hash to its name so that changing the config triggers a rolling restart — a genuinely clever touch that solves a problem Helm leaves you to handle yourself.
configMapGenerator:
- name: app-config
files:
- config/app.properties4 Where it’s weaker than Helm
Honesty time. Kustomize has no notion of conditionals or loops, and that’s deliberate — but it means there’s no neat way to say “create this Ingress only if enabled” or “generate N near-identical objects.” You end up with more overlay files instead of fewer template branches, which is a trade, not a free lunch. It has no packaging or distribution story: there’s no equivalent of a Helm registry to helm install someone-elses-thing. And many third-party projects ship only a Helm chart, so if you want their software you’re consuming Helm whether you like it or not. The pragmatic answer is to use Kustomize for your config and Helm for other people’s packaged apps — and Kustomize can even post-render Helm output, so the two aren’t strictly enemies.
5 Is it worth it?
If your manifests are basically the same across environments with a handful of differences — replica counts, hostnames, image tags, resource limits — Kustomize is the calmer choice, and it’s already in your kubectl. You keep real YAML that your editor and CI can validate, you avoid the template-debugging tax, and the diffs in code review are legible. If you genuinely need conditionals, loops, or to distribute an application for strangers to install, Helm earns its complexity. Most internal platform teams want the first thing and reach reflexively for the second. Try the overlay model before you commit to learning a templating language — you may find you never needed it.




