Kustomize
Kubernetes-native configuration management tool for customising application deployments without templating.
Kustomize Cheatsheet
Kubernetes-native configuration management tool for customising application deployments without templating.
Overview
Kustomize allows you to customise raw, template-free YAML files for multiple purposes, leaving the original YAML untouched and usable as-is. It uses a layered approach with bases and overlays to manage environment-specific configurations.
graph TB
subgraph "Kustomize Architecture"
B[Base Resources] --> K[kustomization.yaml]
P[Patches] --> K
G[Generators] --> K
T[Transformers] --> K
K --> O[Final Manifests]
end
subgraph "Environment Overlays"
B --> DEV[dev overlay]
B --> STG[staging overlay]
B --> PRD[production overlay]
DEV --> DEVK[kustomization.yaml]
STG --> STGK[kustomization.yaml]
PRD --> PRDK[kustomization.yaml]
end
Base and Overlay Structure
Key Concepts
- Base: A directory containing a
kustomization.yamlfile with shared resources - Overlay: A directory that references and customises a base for specific environments
- Resource: Any Kubernetes manifest file (Deployment, Service, ConfigMap, etc.)
- Kustomization: The
kustomization.yamlfile that orchestrates all customisations
Directory Structure
graph LR
subgraph "Typical Project Structure"
ROOT[project/] --> BASE[base/]
ROOT --> OVERLAYS[overlays/]
BASE --> BK[kustomization.yaml]
BASE --> DEP[deployment.yaml]
BASE --> SVC[service.yaml]
OVERLAYS --> DEV[dev/]
OVERLAYS --> STG[staging/]
OVERLAYS --> PRD[production/]
DEV --> DEVK[kustomization.yaml]
STG --> STGK[kustomization.yaml]
PRD --> PRDK[kustomization.yaml]
end
Examples
Base kustomization.yaml:
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
labels:
- pairs:
app: myapp
namespace: default
Base deployment.yaml:
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Development overlay:
# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: dev
namePrefix: dev-
labels:
- pairs:
environment: development
replicas:
- name: myapp
count: 1
images:
- name: myapp
newTag: dev-latest
Production overlay:
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namespace: production
namePrefix: prod-
labels:
- pairs:
environment: production
replicas:
- name: myapp
count: 3
images:
- name: myapp
newTag: v1.2.3
patches:
- path: increase-resources.yaml
Patching Strategies
Key Concepts
- Strategic Merge Patch: Merges patches with original resources using Kubernetes strategic merge rules
- JSON Patch (RFC 6902): Precise operations (add, remove, replace, move, copy, test)
- JSON 6902 Patch: JSON patch with target selector for identifying resources
- Inline Patches: Patches defined directly in kustomization.yaml
Patching Flow
flowchart LR
subgraph "Patch Application Order"
R[Original Resource] --> SMP[Strategic Merge Patches]
SMP --> JP[JSON Patches]
JP --> TR[Transformers]
TR --> F[Final Resource]
end
Strategic Merge Patch Examples
Separate file patch:
# overlays/production/increase-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
resources:
requests:
memory: "256Mi"
cpu: "500m"
limits:
memory: "512Mi"
cpu: "1000m"
Inline strategic merge patch:
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 5
template:
spec:
containers:
- name: myapp
env:
- name: LOG_LEVEL
value: "debug"
JSON Patch Examples
JSON 6902 patch with target:
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
group: apps
version: v1
kind: Deployment
name: myapp
patch: |-
- op: replace
path: /spec/replicas
value: 5
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: NEW_VAR
value: "new-value"
- op: remove
path: /spec/template/spec/containers/0/resources/limits
External JSON patch file:
# kustomization.yaml
patches:
- path: patches/deployment-patch.yaml
target:
kind: Deployment
name: myapp
# patches/deployment-patch.yaml
- op: replace
path: /spec/replicas
value: 3
- op: add
path: /metadata/annotations
value:
custom.io/managed-by: kustomize
Wildcard targeting:
# Apply to all Deployments
patches:
- target:
kind: Deployment
patch: |-
- op: add
path: /metadata/annotations/managed-by
value: kustomize
# Apply using label selector
patches:
- target:
kind: Deployment
labelSelector: "app=myapp"
patch: |-
- op: replace
path: /spec/replicas
value: 2
ConfigMap and Secret Generators
Key Concepts
- Generators: Create ConfigMaps and Secrets from files, literals, or environment files
- Hash Suffix: Automatically append content hash to names for rolling updates
- Behaviour Options: Create, replace, or merge with existing resources
Examples
ConfigMap from literals:
# kustomization.yaml
configMapGenerator:
- name: app-config
literals:
- DATABASE_HOST=localhost
- DATABASE_PORT=5432
- LOG_LEVEL=info
ConfigMap from files:
configMapGenerator:
- name: app-config
files:
- config.properties
- settings.json
- name: nginx-config
files:
- nginx.conf=configs/custom-nginx.conf
ConfigMap from env file:
configMapGenerator:
- name: app-env
envs:
- .env.production
Secret generators:
secretGenerator:
- name: db-credentials
literals:
- username=admin
- password=secretpassword
type: Opaque
- name: tls-secret
files:
- tls.crt
- tls.key
type: kubernetes.io/tls
- name: docker-registry
files:
- .dockerconfigjson=docker-config.json
type: kubernetes.io/dockerconfigjson
Generator options:
configMapGenerator:
- name: app-config
literals:
- KEY=value
options:
disableNameSuffixHash: true
labels:
app: myapp
annotations:
note: "Generated by Kustomize"
generatorOptions:
disableNameSuffixHash: false
labels:
generated: "true"
annotations:
generator: kustomize
Behaviour options:
configMapGenerator:
- name: existing-config
behavior: merge # create | replace | merge
literals:
- NEW_KEY=new-value
Common Transformers
Key Concepts
- Transformers: Modify resources globally across all manifests
- Built-in Transformers: namePrefix, nameSuffix, namespace, labels, commonAnnotations (
commonLabelsis deprecated in kustomize v5 in favour oflabels) - Image Transformer: Update container images without patching
- Replica Transformer: Change replica counts
Examples
Name transformers:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
# Add prefix to all resource names
namePrefix: prod-
# Add suffix to all resource names
nameSuffix: -v2
Namespace transformer:
# Set namespace for all resources
namespace: production
Label and annotation transformers:
# Add labels to all resources (set includeSelectors: true to also
# inject into selectors — the old commonLabels behaviour)
labels:
- pairs:
app.kubernetes.io/name: myapp
app.kubernetes.io/version: "1.0.0"
environment: production
# Add annotations to all resources
commonAnnotations:
owner: platform-team
contact: platform@example.com
Image transformer:
images:
# Change image tag
- name: myapp
newTag: v2.0.0
# Change image name and tag
- name: nginx
newName: my-registry/nginx
newTag: "1.21"
# Use digest instead of tag
- name: redis
newName: redis
digest: sha256:abc123def456...
Replica transformer:
replicas:
- name: myapp-deployment
count: 5
- name: worker-deployment
count: 3
Custom transformer configurations:
# kustomization.yaml
transformers:
- label-transformer.yaml
# label-transformer.yaml
apiVersion: builtin
kind: LabelTransformer
metadata:
name: custom-labels
labels:
custom-label: custom-value
fieldSpecs:
- path: metadata/labels
create: true
Integration with kubectl
Common Commands
# Build and preview manifests
kustomize build overlays/production
# Apply using kustomize build
kustomize build overlays/production | kubectl apply -f -
# Apply using kubectl's built-in kustomize
kubectl apply -k overlays/production
# Preview what would be applied
kubectl apply -k overlays/production --dry-run=client -o yaml
# Delete resources
kubectl delete -k overlays/production
# Diff against current state
kubectl diff -k overlays/production
# View specific resource
kustomize build overlays/production | kubectl get -f - -o yaml
Advanced kubectl Integration
# Apply with server-side apply
kubectl apply -k overlays/production --server-side
# Apply with pruning (remove deleted resources)
kubectl apply -k overlays/production --prune -l app=myapp
# Create namespace if needed
kubectl create namespace production --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -k overlays/production
# Validate manifests
kustomize build overlays/production | kubectl apply --dry-run=server -f -
# Export to file
kustomize build overlays/production > production-manifests.yaml
# Use with specific kubeconfig
KUBECONFIG=~/.kube/prod-config kubectl apply -k overlays/production
CI/CD Integration
#!/bin/bash
# Example CI/CD script
# Validate kustomization
kustomize build overlays/$ENVIRONMENT > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Kustomization build failed"
exit 1
fi
# Check for drift
kubectl diff -k overlays/$ENVIRONMENT
DIFF_EXIT=$?
if [ $DIFF_EXIT -eq 0 ]; then
echo "No changes to apply"
elif [ $DIFF_EXIT -eq 1 ]; then
echo "Changes detected, applying..."
kubectl apply -k overlays/$ENVIRONMENT
else
echo "Error checking diff"
exit 1
fi
Best Practices for Organisation
Key Concepts
- DRY Principle: Keep common configurations in bases
- Environment Parity: Minimise differences between environments
- Version Control: Track all kustomization files in git
- Validation: Always validate before applying
Recommended Directory Structure
project/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
├── components/
│ ├── monitoring/
│ │ ├── kustomization.yaml
│ │ └── service-monitor.yaml
│ ├── logging/
│ │ ├── kustomization.yaml
│ │ └── fluent-bit.yaml
│ └── security/
│ ├── kustomization.yaml
│ └── network-policy.yaml
├── overlays/
│ ├── dev/
│ │ ├── kustomization.yaml
│ │ └── patches/
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ └── patches/
│ └── production/
│ ├── kustomization.yaml
│ ├── patches/
│ └── secrets/
└── README.md
Using Components
# components/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
resources:
- service-monitor.yaml
configMapGenerator:
- name: prometheus-config
files:
- prometheus.yml
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
components:
- ../../components/monitoring
- ../../components/logging
Best Practice Examples
Use vars for cross-resource references (deprecated, use replacements):
# Modern approach with replacements
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
replacements:
- source:
kind: Service
name: myapp
fieldPath: metadata.name
targets:
- select:
kind: Deployment
name: myapp
fieldPaths:
- spec.template.spec.containers.[name=myapp].env.[name=SERVICE_NAME].value
Organise patches by purpose:
# overlays/production/kustomization.yaml
patches:
# Resource scaling
- path: patches/replicas.yaml
# Security configurations
- path: patches/security-context.yaml
# Environment-specific configs
- path: patches/env-vars.yaml
Version pinning for remote bases:
resources:
# Pin to specific version/tag
- github.com/organisation/repo/base?ref=v1.2.3
# Or specific commit
- github.com/organisation/repo/base?ref=abc123
Quick Reference
| Operation | Command/Configuration | Description |
|---|---|---|
| Build manifests | kustomize build <dir> |
Generate final YAML |
| Apply with kubectl | kubectl apply -k <dir> |
Apply kustomization |
| Preview changes | kubectl diff -k <dir> |
Show differences |
| Delete resources | kubectl delete -k <dir> |
Remove resources |
| Add resource | resources: [file.yaml] |
Include manifest |
| Add patch | patches: [{path: patch.yaml}] |
Apply modification |
| Set namespace | namespace: <name> |
Override namespace |
| Add prefix | namePrefix: <prefix>- |
Prefix all names |
| Add suffix | nameSuffix: -<suffix> |
Suffix all names |
| Update image | images: [{name: x, newTag: y}] |
Change image tag |
| Set replicas | replicas: [{name: x, count: n}] |
Change replica count |
| Add labels | labels: [{pairs: {key: value}}] |
Label all resources |
| Generate ConfigMap | configMapGenerator: [...] |
Create ConfigMap |
| Generate Secret | secretGenerator: [...] |
Create Secret |
| Include component | components: [path/] |
Add reusable component |
| Remote base | resources: [url?ref=tag] |
Use remote kustomization |
Common Issues and Solutions
Issue: Hash suffix causing reference mismatches
Problem: ConfigMap/Secret name hash changes but dependent resources don't update.
Solution: Kustomize automatically updates references in the same kustomization. Ensure all dependent resources are in the same build.
# This works automatically
configMapGenerator:
- name: app-config
literals:
- KEY=value
# References are updated automatically
# deployment.yaml
envFrom:
- configMapRef:
name: app-config # Will be updated to app-config-<hash>
Issue: Patches not applying correctly
Problem: Strategic merge patch doesn't produce expected result.
Solution: Ensure correct API version and kind, and use proper merge keys.
# Correct: Include all required identifiers
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp # Must match exactly
spec:
template:
spec:
containers:
- name: myapp # Container name is the merge key
resources:
limits:
memory: "512Mi"
Issue: Resources not found in base
Problem: error: accumulating resources: resource not found.
Solution: Verify relative paths and ensure files exist.
# Check path is correct relative to kustomization.yaml
resources:
- ../../base # Must exist and contain kustomization.yaml
Issue: Duplicate resources
Problem: may not add resource with an already registered id.
Solution: Ensure resources are only included once across bases and overlays.
# Wrong: Including same resource twice
resources:
- ../../base
- ../../base/deployment.yaml # Already included via base
# Correct: Only include the base
resources:
- ../../base
Issue: commonLabels breaking selector immutability
Problem: Applying the deprecated commonLabels to an existing Deployment fails because it also injects the labels into immutable selectors.
Solution: Use the labels field, which only touches selectors when you ask it to.
# Adds labels to metadata and pod templates without touching selectors
labels:
- pairs:
new-label: value
includeTemplates: true
# (includeSelectors: true would reproduce the old commonLabels behaviour)
Issue: Secret values not being encoded
Problem: Secret generator not base64-encoding values.
Solution: Kustomize automatically encodes literals and file contents. Don't pre-encode.
secretGenerator:
- name: my-secret
literals:
- password=plaintext # Will be base64 encoded automatically
Issue: Remote bases not updating
Problem: Changes to remote base not reflected in builds.
Solution: Pin to a specific commit or tag and update the ref deliberately — kustomize fetches remote bases per build, so unexpected staleness usually means a floating ref resolved elsewhere (e.g. a CI mirror or proxy).
# Pin to specific version
resources:
- github.com/org/repo/base?ref=v1.2.3
Issue: kubectl apply -k differs from kustomize build
Problem: Different versions produce different results.
Solution: Check kubectl's embedded kustomize version and consider using standalone kustomize.
# Check kubectl's kustomize version
kubectl version --client -o yaml | grep kustomizeVersion
# Use standalone kustomize for consistency
kustomize build overlays/prod | kubectl apply -f -
Related Topics
- Helm: Kubernetes package manager with templating - complements Kustomize for chart-based deployments
- ArgoCD: GitOps continuous delivery tool with native Kustomize support
- Kubernetes RBAC: Role-based access control for securing deployments managed by Kustomize
- GitOps Workflows: Best practices for managing infrastructure as code with Git
- Kubernetes Secrets Management: Tools like Sealed Secrets or External Secrets for secure secret handling
- Kubernetes Admission Controllers: Validating and mutating webhooks for policy enforcement on Kustomize outputs