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
EKS vs Self-Managed Kubernetes
EKS runs the control plane for you; you still own nodes, networking, and add-ons. Here's the division of responsibility, the node options, and when self-managed is worth it.
September 11, 2026The AWS Load Balancer Controller
On EKS, the AWS Load Balancer Controller turns Ingress objects into ALBs and LoadBalancer Services into NLBs. Here's target types, ALB sharing, and the IAM/subnet-tag gotchas.
September 7, 2026Pod Security Admission
PSP is gone. Pod Security Admission enforces the Pod Security Standards via namespace labels — three levels, three modes. Here's how to roll it out without breaking things.
August 30, 2026