Kustomize
Summary
- Kustomize is a template-free customization tool that builds environment variants by patching a shared base of real YAML.
- Insight: Because every layer is valid Kubernetes YAML, overlays produce small, reviewable diffs and never emit malformed intermediate output.
- Key Concepts: base, overlay,
kustomization.yaml, strategic-merge patch, JSON 6902 patch, generators, transformers. - When to Use: When you own your manifests and want per-environment promotion without a templating language.
- Limitations: No general variable substitution; wide parameterization and packaging are weaker than Helm.
- Related: Templating vs overlaying, Helm charts, GitOps promotion, ADR selection.
Recipe
Put complete manifests in a base/ directory with a kustomization.yaml that lists them.
Create one overlays/<env>/ directory per environment, each with a kustomization.yaml that references the base and records only that environment's deltas.
Build any environment with kubectl kustomize or apply it directly.
kubectl kustomize overlays/production # print rendered YAML
kubectl apply -k overlays/production # build and applyWorking Example
The base holds real, applyable manifests.
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: app
image: registry.example.com/app:1.8.3
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yamlThe production overlay records only what differs.
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: prod
namePrefix: prod-
commonLabels:
environment: production
resources:
- ../../base
images:
- name: registry.example.com/app
newTag: 1.8.3
replicas:
- name: app
count: 5
patches:
- target:
kind: Deployment
name: app
patch: |-
- op: add
path: /spec/template/spec/containers/0/resources/limits/cpu
value: "1"A staging overlay might reuse the same base with replicas: 2 and no CPU limit patch, keeping the two environments' differences explicit.
Deep Dive
Strategic-merge vs JSON 6902 patches
A strategic-merge patch is a partial manifest that Kustomize merges by field; it is the most readable option for adding or changing map fields.
# overlays/production/patch-env.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
containers:
- name: app
env:
- name: LOG_LEVEL
value: warnA JSON 6902 patch uses explicit op/path operations and is best for list edits and precise insertions or removals.
Reference merge patches from patchesStrategicMerge or the unified patches field with a target selector.
Built-in transformers
namePrefix and nameSuffix rename every object consistently, and Kustomize rewrites references (a Deployment's ConfigMap reference follows the rename).
commonLabels and commonAnnotations stamp metadata across all resources, and namespace moves the whole set into one namespace.
The images transformer swaps tags or repositories without editing the base, which is the idiomatic way GitOps tools bump image versions.
Generators
configMapGenerator and secretGenerator build ConfigMaps and Secrets from files or literals and append a content hash to the name.
# overlays/production/kustomization.yaml (excerpt)
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=warn
- FEATURE_X=onThe hash suffix means editing the config produces a new object name, so dependent Deployments roll automatically - a built-in fix for stale config.
Set generatorOptions.disableNameSuffixHash: true only when a stable name is required, accepting that you lose the automatic rollout.
Composition and components
An overlay can reference multiple bases and other overlays, letting you layer shared concerns like a monitoring sidecar across services.
Reusable kind: Component fragments apply optional, cross-cutting changes (for example, adding a NetworkPolicy) to any kustomization that lists them under components.
Gotchas
Editing the base to fix one environment defeats the model; put environment-specific changes in the overlay instead.
Strategic-merge cannot reliably reorder or delete specific list items; switch to a JSON 6902 patch for list surgery.
Disabling the generator hash suffix reintroduces the classic problem where a ConfigMap change does not restart pods.
Kustomize does not fetch remote resources by default in a hermetic build; vendor charts or use helmCharts deliberately and pin versions.
commonLabels also writes to selector fields, which are immutable on existing Deployments; adding one to a live workload can force a replace.
Alternatives
Helm suits redistributable software and wide parameterization through a values file and templating; see the Helm pages.
A Helm plus Kustomize hybrid packages with Helm and post-renders with Kustomize for last-mile, site-specific tweaks.
Plain manifests fit a single trivial environment where even overlays add ceremony.
Higher-level config languages like CUE or jsonnet offer stronger typing when your customization logic outgrows patches.
FAQs
Do I need a separate binary? No. Kustomize is built into kubectl via apply -k and kubectl kustomize. A standalone kustomize CLI exists for newer features.
How do overlays change images? Use the images transformer with newTag or newName; no base edit is needed.
Why does my ConfigMap change not roll pods? Ensure the generator hash suffix is enabled so the ConfigMap name changes and triggers a rollout.
Can one overlay use several bases? Yes. List multiple entries under resources, including other overlays and components.
When do I use a JSON 6902 patch over strategic-merge? For precise list edits, insertions, and deletions that merge semantics cannot express.
Can Kustomize consume Helm charts? Yes, via the helmCharts field, letting you overlay a rendered chart.
Related
- Templating vs Overlaying Manifests
- Helm Basics
- Chart Design
- Helm vs Kustomize ADR
- Helm Best Practices
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).