Kustomize Basics
Introduction¶
Kustomize (built into kubectl -k and kubectl apply -k) customizes Kubernetes
manifests without templates. You keep valid YAML and layer changes on top
via overlays. It's the lighter alternative to Helm when you don't need
distribution or complex logic.
Layout¶
base/
├── kustomization.yaml
├── deployment.yaml
├── service.yaml
overlays/
├── staging/
│ ├── kustomization.yaml
│ └── replicas-patch.yaml
└── prod/
├── kustomization.yaml
├── replicas-patch.yaml
└── resources-patch.yaml
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: myapp
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: prod
namePrefix: prod-
resources:
- ../../base
patches:
- path: replicas-patch.yaml
- path: resources-patch.yaml
images:
- name: myapp
newTag: 1.4.2 # bump this in CI on release
replicas:
- name: myapp
count: 6
labels:
- pairs: { env: prod }
includeSelectors: false
kubectl kustomize overlays/prod # render to stdout
kubectl apply -k overlays/prod
Patches¶
Strategic merge — a partial manifest; fields you specify override, lists
merge by key (usually name):
# replicas-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: myapp }
spec:
template:
spec:
containers:
- name: myapp # matched by name
resources:
requests: { cpu: "500m", memory: "512Mi" }
JSON 6902 — precise operations, needed for list-by-index or remove:
patches:
- target: { kind: Deployment, name: myapp }
patch: |
- op: add
path: /spec/template/spec/containers/0/env/-
value: { name: LOG_LEVEL, value: debug }
- op: replace
path: /spec/strategy/type
value: Recreate
Generators¶
configMapGenerator:
- name: app-config
files: [config/app.yaml]
literals: [LOG_LEVEL=info]
secretGenerator:
- name: app-secret
envs: [secret.env] # keep this file out of git / use SOPS
generatorOptions:
disableNameSuffixHash: false # default: appends a content hash to the name
The name suffix hash is a feature: change the ConfigMap content → the
generated name changes → the Deployment referencing it changes → a rollout
happens automatically. Downside: anything referencing the ConfigMap by a fixed
name (outside kustomize) breaks. disableNameSuffixHash: true if you need a
stable name (and then handle rollouts yourself).
Components¶
Reusable, optional feature bundles (a monitoring sidecar, an mTLS config) that
overlays can components: in — DRY across many overlays.
Verification and troubleshooting¶
kubectl kustomize overlays/prod | less # what will be applied
kubectl kustomize overlays/prod | kubectl diff -f -
kustomize build overlays/prod --enable-helm # if you inline Helm charts
- Patch not applied —
metadata.namein the patch doesn't match the base resource (including anynamePrefix— patches target the pre-prefix name), wrongapiVersion/kind, or the field path is wrong (JSON patch). Render and diff. - "no matches for Id ..." — the patch targets a resource not in
resources, or the name/kind is off. For JSON patches use the exacttarget:. - ConfigMap name has a weird
-abc123suffix and a Service/other ref can't find it — the generator hash. If the reference is inside kustomize it's rewritten automatically; if it's external,disableNameSuffixHash: true. commonLabelsbroke a rolling update —commonLabels(old) also sets selector labels, and selectors are immutable. Use the newerlabels:withincludeSelectors: falsefor labels that shouldn't touch the selector.- Overlay pulls a stale base — remote bases (
resources: - github.com/...?ref=...) are cached; pin a ref andkustomize buildwith--load-restrictor/ clear the cache. - Image tag not overridden — the container's
image:name in the base must match theimages: - name:exactly (repo path included or not, consistently). - Helm chart inflation fails — needs
--enable-helmand thehelmCharts:field; kustomize's Helm support is limited (no hooks).
Related tools and reading¶
- On-site: YAML ↔ JSON converter, JSON formatter.
- Related posts: Helm chart anatomy, GitOps with Argo CD.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Docker & Kubernetes
StatefulSets Explained
StatefulSets give pods sticky names, sticky storage, and ordered operations — what databases and clustered apps need. Here's how each guarantee works.
October 24, 2026Persistent Volumes and Claims, Explained
PVC is a request, PV is the fulfilled storage, StorageClass is the factory. Most 'Pending PVC' problems are an access-mode or provisioner mismatch.
October 24, 2026Kubernetes Jobs and CronJobs
A Job runs pods until they succeed; a CronJob creates Jobs on a schedule. Here's parallelism, retry limits, the missed-schedule trap, and history cleanup.
October 24, 2026