Kustomize Cheat Sheet
Kustomize is great until you need a list patch, then it's terrible until you remember JSON 6902 exists. The patterns that hold up after the third refactor.
Bases & overlays
The directory shape is the contract. Get it right once and adding environments is trivial; get it wrong and you'll keep rewriting paths.
- Base contains
kustomization.yaml+ raw resources (deployment, service, ingress). No environment-specific values. - Overlays sit in
overlays/dev,overlays/staging,overlays/prod. Each has its ownkustomization.yamlwithresources: [../../base]. kubectl kustomize overlays/prod, render an overlay to stdout. The pre-deploy preview.kubectl apply -k overlays/prod, render and apply. The deploy verb.namePrefix: prod-andnameSuffix: -v2in an overlay rename every resource. Useful for blue/green or shadow stacks.commonLabels:andcommonAnnotations:stamp labels onto every resource. Setenv: prodonce instead of in 12 files.
Patches, strategic merge
Strategic merge is the default. It's YAML, it merges by key, and it's good enough for 80% of cases.
- Inline form,
patches: - path: replicas.yamlin the overlay'skustomization.yaml. - Patch file shape, same kind/name/namespace as the target, only the fields you're changing:
apiVersion: apps/v1
kind: Deployment
metadata: { name: api }
spec: { replicas: 5 } - Maps merge by key. So patching
spec.template.spec.containersneeds the containernameas the merge key. - Lists do NOT merge by default, they replace. This is the classic surprise. Use JSON 6902 or set the
x-kubernetes-patch-merge-key. - Use
$patch: deleteon a list item to remove it instead of replacing. Useful for stripping sidecars in non-prod overlays. - Use
$patch: replaceon a map when you want full replacement instead of merge. Rare but useful for env vars.
Patches, JSON 6902
When strategic merge fights you (lists are the usual culprit), drop to JSON 6902 patches. They use JSON Pointer paths and operate field-by-field.
- Six ops,
add,remove,replace,move,copy,test.testaborts the patch if the value doesn't match; the safety net. - Add a container env var,
op: add, path: /spec/template/spec/containers/0/env/-, value: { name: LOG_LEVEL, value: debug }. The-at the end means "append". - Replace a container image,
op: replace, path: /spec/template/spec/containers/0/image, value: api:v1.4.2. - Remove an annotation,
op: remove, path: /metadata/annotations/old-key. Slashes in keys must be escaped as~1. - Inline patch in
kustomization.yaml:patches:
- target: { kind: Deployment, name: api }
patch: |-
- op: replace
path: /spec/replicas
value: 10 - Use
target.labelSelectorto apply one patch to many resources,target: { kind: Deployment, labelSelector: "tier=web" }.
Generators, configMap & secret
Generators build ConfigMaps and Secrets at render time, hash-suffix them, and update every reference automatically. Pods restart on config change without you tracking it.
configMapGenerator: - name: app-config, from a file. Renders as
files: [config.yaml]app-config-7t2k4fwith content hash.configMapGenerator: - name: app-env, from literals. Same hash-suffix behavior.
literals: [LOG_LEVEL=info, FEATURE_X=true]secretGenerator: - name: db-creds, from a dotenv file. Don't commit
envs: [.env.prod].env.prod.- Disable hash suffix with
generatorOptions: { disableNameSuffixHash: true }. Loses the auto-restart benefit; use only when external systems reference the config by fixed name. - Generators replace the entire ConfigMap on any change. To merge into an existing one, use
behavior: mergein the overlay. behavior: replaceoverwrites a base ConfigMap completely from the overlay. Use sparingly, surprising for readers.
vars & replacements
Vars are the legacy variable system; replacements is the modern replacement (Kustomize 4.5+). Use replacements in any new code.
replacements: - source: { kind: Service, name: api, fieldPath: metadata.name }, pin Ingress backend to Service name; rename once.
targets: [{ select: { kind: Ingress }, fieldPaths: [spec.rules.0.http.paths.0.backend.service.name] }]- Source can also be a ConfigMap field, an annotation, or a label. Anything addressable by JSON Pointer.
options: { delimiter: ":", index: 1 }, rewrite a substring of the target value. Useful for image tag bumps.- Vars (older),
vars: - name: DB_HOST. Then reference as
objref: { kind: Service, name: db }
fieldref: { fieldpath: metadata.name }$(DB_HOST)in a resource. - Vars only work where the field has been registered (fieldspecs), this is the source of "var didn't substitute" pain.
- Migrate vars to replacements when you can. Replacements work everywhere without a fieldspec list.
Components
Components are reusable bundles of patches and resources you mix into multiple overlays. Think "logging sidecar I want on every prod deployment".
- A component looks like a base except
kind: Componentin itskustomization.yaml. Otherwise the syntax is identical. - Reference from an overlay,
components: [../../components/logging-sidecar]. Components apply after base resources. - Order matters, components run in list order; later ones can patch earlier ones.
- Use components for cross-cutting concerns: logging sidecars, mTLS sidecars, audit annotations, NetworkPolicies. Anything you'd otherwise copy-paste.
- Components can add resources too, not just patch. A "monitoring" component can ship its own ServiceMonitor.
- Don't put environment values in components, that's overlay territory. Components stay environment-agnostic.