Kustomize cheatsheet.
Structure
manifests/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ...
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── patches.yaml
├── staging/
└── prod/
base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- ingress.yaml
commonLabels:
app: web
commonAnnotations:
managed-by: kustomize
images:
- name: myapp
newTag: v1.0.0
overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: prod
resources:
- ../../base
namePrefix: prod-
nameSuffix: -v1
replicas:
- name: web
count: 5
images:
- name: myapp
newName: ghcr.io/me/myapp
newTag: ${GIT_SHA}
patches:
- path: resources-patch.yaml
target:
kind: Deployment
name: web
- patch: |-
- op: replace
path: /spec/replicas
value: 10
target:
kind: Deployment
name: web
configMapGenerator:
- name: app-config
behavior: merge
literals:
- LOG_LEVEL=info
- ENV=production
secretGenerator:
- name: app-secrets
envs:
- secrets.env
Build / apply
kubectl kustomize overlays/prod # render to stdout
kubectl apply -k overlays/prod
Or:
kustomize build overlays/prod | kubectl apply -f -
Patches
Strategic merge patch:
# resources-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
template:
spec:
containers:
- name: web
resources:
limits: { cpu: 2, memory: 1Gi }
JSON patch (in kustomization.yaml):
patches:
- patch: |-
- op: add
path: /spec/template/spec/containers/0/env/-
value: { name: NEW, value: "x" }
target: { kind: Deployment, name: web }
ConfigMap / Secret generators
configMapGenerator:
- name: app-config
files:
- config.yaml
- app.properties
literals:
- LOG_LEVEL=info
secretGenerator:
- name: db-secret
literals:
- password=supersecret
type: Opaque
generatorOptions:
disableNameSuffixHash: false # default false; suffix hashes for atomic updates
Hash suffix triggers pod restart on config change (good).
Components
Reusable units:
components/
└── monitoring/
├── kustomization.yaml
├── prometheus-rules.yaml
└── servicemonitor.yaml
# components/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
- prometheus-rules.yaml
- servicemonitor.yaml
# overlays/prod/kustomization.yaml
components:
- ../../components/monitoring
vars (deprecated, use replacements)
replacements:
- source:
kind: ConfigMap
name: app-config
fieldPath: data.HOST
targets:
- select: { kind: Deployment, name: web }
fieldPaths: [spec.template.spec.containers.0.env.[name=HOST].value]
Helm + Kustomize
helmCharts:
- name: ingress-nginx
repo: https://kubernetes.github.io/ingress-nginx
version: 4.10.0
releaseName: ingress
namespace: ingress-nginx
valuesFile: ingress-values.yaml
kubectl kustomize --enable-helm overlays/prod
Common patterns
Dev / staging / prod variants
# overlays/dev: 1 replica, no ingress
# overlays/staging: 2 replicas, internal ingress
# overlays/prod: 5 replicas, public ingress + cert-manager
Single base, three overlays.
Multi-tenant
Per-customer overlay with namespace + branding configmap.
Common mistakes
- Editing base instead of patching from overlay.
- ConfigMap hash invalidating on every build (forgot
behavior: replace). - patches.yaml referencing wrong resource name (post namePrefix).
- Mixing helm and kustomize naïvely → conflicts.
- Forgetting
namespace:in overlay → applies to wrong ns.
kubectl kustomize vs apply -k
# Print:
kubectl kustomize overlays/prod
# Apply:
kubectl apply -k overlays/prod
# Diff:
kubectl diff -k overlays/prod
Read this next
If you want my Kustomize template structure, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .