Kustomize Reference
Overview
Kustomize is a Kubernetes-native configuration management tool that lets you customize raw YAML manifests without templating. It works on a base + overlay model: you define a base set of resources and layer environment-specific changes on top via overlays. Kustomize is built into kubectl (kubectl apply -k) and is the foundation of GitOps workflows with Argo CD and Flux.
Directory layout
The base + overlays layout is the core organizing principle of Kustomize. The base holds resources shared by every environment; each overlay directory references that base and layers environment-specific patches and settings on top of it.
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── overlays/
├── staging/
│ ├── kustomization.yaml
│ └── patch-replicas.yaml
└── production/
├── kustomization.yaml
├── patch-replicas.yaml
└── patch-resources.yaml
kustomization.yaml structure
Minimal base
At minimum, a kustomization.yaml lists the resources to include. This is enough for a base whose manifests are deployed unchanged across all environments.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
Full-featured base
A full base can do much more: set a namespace for every resource, stamp shared labels and annotations across the whole set, prefix or suffix resource names, and override image tags — all before any overlay is involved.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Namespace to set on all resources (unless overridden)
namespace: default
# Resource files or directories to include
resources:
- deployment.yaml
- service.yaml
- ingress.yaml
# Labels applied to all resources and selectors
commonLabels:
app.kubernetes.io/name: my-app
app.kubernetes.io/part-of: my-platform
# Annotations applied to all resources
commonAnnotations:
managed-by: kustomize
team: platform
# Prefix/suffix for resource names
namePrefix: my-app-
nameSuffix: -v2
# Image tag overrides
images:
- name: my-app
newTag: "1.5.0"
- name: sidecar-proxy
newName: my-registry/sidecar-proxy
newTag: "2.3.1"
digest: sha256:abc123...
Overlays
Overlays are directories that reference a base and apply patches:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Reference the base directory (relative path)
resources:
- ../../base
# Override the namespace
namespace: staging
# Apply patches
patches:
- path: patch-replicas.yaml
target:
kind: Deployment
name: my-app
- path: patch-resources.yaml
target:
kind: Deployment
name: my-app
Patching strategies
Strategic merge patch (default)
Matches on metadata.name and kind, then merges fields. The simplest approach — just write the fields you want to override and specify the target.
# patch-replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 5
# patch-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
JSON Patch (RFC 6902)
Use patchesJson6902 for array operations (add, remove, replace, move, copy):
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: my-app
patch: |-
- op: replace
path: /spec/replicas
value: 3
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: ENVIRONMENT
value: "production"
- op: remove
path: /spec/template/spec/containers/0/resources/limits/cpu
Inline patch
Define the patch directly in the kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
env:
- name: LOG_LEVEL
value: debug
target:
kind: Deployment
name: my-app
ConfigMap and Secret generators
ConfigMap from literals
Instead of hand-writing ConfigMap YAML, use configMapGenerator to build one from key=value literals or files. Kustomize appends a content hash to the generated name, so changing a value automatically triggers a rolling restart of every consumer.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=info
- CACHE_TTL=300
- METRICS_ENABLED=true
- name: app-properties
files:
- application.properties
- config/database.properties
options:
disableNameSuffixHash: false # Append content hash (default: true)
Secret from files and literals
secretGenerator works the same way for Secrets, building them from literals or files such as a TLS keypair or a Docker config. Set type to control the Secret kind — for example, kubernetes.io/dockerconfigjson for registry credentials.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
secretGenerator:
- name: app-secrets
literals:
- DB_PASSWORD=supersecret
- API_KEY=abc-123-def
files:
- tls.key
- tls.crt
type: Opaque
- name: docker-registry-creds
type: kubernetes.io/dockerconfigjson
files:
- .dockerconfigjson
Generated resource naming
When disableNameSuffixHash is false (the default), Kustomize appends a content hash to the name (e.g., app-config-7k5m8f). This triggers rolling updates of Deployments that reference the ConfigMap, since the name changes whenever the content changes.
Common transformers
Name manipulation
namePrefix and nameSuffix transform every resource name in the kustomization. This is how you deploy the same base twice into one cluster — for example, per-team namespaces — without resource-name collisions.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Prefix and suffix applied to all resource names
namePrefix: team-a-
nameSuffix: -prod
# Reference a specific resource by its original name
# Kustomize resolves the prefix/suffix at build time
Replacements (advanced field substitution)
Replacements copy a field from one resource into arbitrary locations of another — for example, injecting a Service's name as an environment variable in a Deployment. This is the modern mechanism that replaces the older vars feature.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Copy a value from one resource to another
replacements:
- source:
kind: Service
name: my-app
fieldPath: metadata.name
targets:
- select:
kind: Deployment
name: my-app
fieldPaths:
- spec.template.spec.containers.[name=my-app].env.[name=SERVICE_NAME].value
Vars (legacy — prefer replacements)
vars was the original way to inject a value from one resource into another, but it required $()-style references in the YAML and had edge cases with quoting and escaping. Keep it in mind only when reading older kustomizations — new configurations should use replacements.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
vars:
- name: SERVICE_NAME
objref:
kind: Service
name: my-app
apiVersion: v1
fieldref:
fieldpath: metadata.name
Image transformation
The images transformer rewrites image names, tags, and digests across every container that references them. It's the standard way to promote a new version through environments or to repoint images at a private registry without editing individual manifests.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
# Override only the tag
- name: my-app
newTag: "2.0.0"
# Override registry/name and tag
- name: nginx
newName: my-private-registry.io/nginx
newTag: "1.25"
# Pin to a specific digest
- name: my-app
digest: sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
Common commands
Kustomize's CLI — also built into kubectl via the -k flag — renders the final manifest set from a base or overlay. Use build to preview the rendered YAML, diff to compare it against the live cluster before applying, and apply -k to deploy.
# Preview rendered manifests (dry run)
kustomize build base/
kustomize build overlays/production/
# Apply to cluster
kubectl apply -k base/
kubectl apply -k overlays/production/
# Apply with server-side dry run
kubectl apply -k overlays/production/ --dry-run=server
# Apply with prune (removes resources not in the kustomization)
kubectl apply -k overlays/production/ --prune -l app.kubernetes.io/part-of=my-platform
# Diff against live cluster
kubectl diff -k overlays/production/
# Validate without applying
kustomize build overlays/production/ | kubectl apply --dry-run=client -f -
Real-world example: base + production overlay
Base: base/deployment.yaml
This is the shared Deployment manifest. It defines the application with modest default resource requests — anything environment-specific (replicas, resources, health checks) is layered on later by overlays.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:latest
ports:
- containerPort: 8080
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
Base: base/service.yaml
The base Service selects the pods by the app: my-app label applied by the base Deployment. When an overlay adds commonLabels or a namePrefix, Kustomize updates the Service's selector and target names consistently.
apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
Base: base/kustomization.yaml
The base kustomization ties the Deployment and Service together, stamps them with shared commonLabels, and pins the base image tag to 1.5.0. Overlays inherit all of this and only change what differs per environment.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app.kubernetes.io/name: my-app
app.kubernetes.io/part-of: platform
resources:
- deployment.yaml
- service.yaml
images:
- name: my-app
newTag: "1.5.0"
Production overlay: overlays/production/kustomization.yaml
The production overlay references the base, then layers on environment-specific changes: a production namespace, a prod- name prefix, an environment label, production config values, and patches that raise replicas and resource limits.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
- ../../base
namePrefix: prod-
commonLabels:
environment: production
patches:
- path: patch-replicas.yaml
- path: patch-resources.yaml
- path: patch-health-check.yaml
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=warn
- METRICS_ENABLED=true
- CACHE_TTL=600
Production patches
The patches the overlay references apply the production-specific values. Each is a small file that overrides only the fields that differ from the base:
overlays/production/patch-replicas.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 6
overlays/production/patch-resources.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2000m"
memory: "2Gi"
overlays/production/patch-health-check.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
Best practices
- Keep bases minimal: Put only what is common across all environments in the base.
- Patch, don't duplicate: Never copy full manifests into overlays — patch only the fields that differ.
- Use
commonLabelsin bases: They flow to all resources and their selectors automatically. - Leverage
configMapGenerator: Content hashes in names trigger automatic rolling updates. - Pin image digests in production: Override tags with
digestfor immutable, reproducible deployments. - Run
kubectl diff -kbefore applying: Preview changes without affecting the cluster.