What is Kustomize?
Kustomize is a template-free way to customize Kubernetes manifests. Built into kubectl, it uses composition and patches instead of templating.
Kustomize vs Helm
| Feature | Kustomize | Helm |
|---|---|---|
| Learning curve | Easier (YAML-based) | Steeper (Go templates) |
| Templating | No (composition instead) | Yes (Go templates) |
| Releases/Versions | Not built-in | Built-in |
| Dependencies | Limited | Full support |
| GitOps-friendly | Very | Yes |
Basic Directory Structure
my-app/
├── base/ # Base, environment-agnostic manifests
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── overlays/ # Environment-specific overlays
├── dev/
│ └── kustomization.yaml
├── staging/
│ └── kustomization.yaml
└── prod/
└── kustomization.yaml
Base Configuration
Base kustomization.yaml
The base contains environment-agnostic resources:
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: default
commonLabels:
app: myapp
managed-by: kustomize
commonAnnotations:
description: "My application"
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
replicas:
- name: myapp
count: 3Base deployment.yaml
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3 # Will be overridden by kustomization.yaml
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: log-level
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512MiBase service.yaml
# base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
selector:
app: myappBase configmap.yaml
# base/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
log-level: "info"
database-host: "db.default.svc.cluster.local"
api-timeout: "30"Overlays
Overlays customize the base for specific environments.
Development Overlay
# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: development
bases:
- ../../base
namePrefix: dev-
commonLabels:
environment: dev
replicas:
- name: myapp
count: 1 # Only 1 replica in dev
patchesStrategicMerge:
- deployment-patch.yaml
configMapGenerator:
- name: app-config
files:
- configmap-dev.env
behavior: create# overlays/dev/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: app
image: myapp:dev
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 128Mi# overlays/dev/configmap-dev.env
LOG_LEVEL=debug
DATABASE_HOST=localhost:5432
API_TIMEOUT=10Production Overlay
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
bases:
- ../../base
namePrefix: prod-
commonLabels:
environment: prod
replicas:
- name: myapp
count: 10 # 10 replicas in prod
patchesStrategicMerge:
- deployment-patch.yaml
- service-patch.yaml
resources:
- ingress.yaml
- pdb.yaml
secretsGenerator:
- name: db-secret
envs:
- secret.env
behavior: create# overlays/prod/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: app
image: myapp:v1.0.0
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi# overlays/prod/service-patch.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
type: LoadBalancer# overlays/prod/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: prod-myapp
port:
number: 80# overlays/prod/pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: myapp
spec:
minAvailable: 5
selector:
matchLabels:
app: myappUsing Kustomize
Preview Manifests
# Kustomize output (without applying)
kustomize build overlays/dev
kustomize build overlays/prod
# Or with kubectl
kubectl kustomize overlays/dev
kubectl kustomize overlays/prodDeploy
# Dev environment
kubectl apply -k overlays/dev
# Prod environment
kubectl apply -k overlays/prodUpdate Image
# In overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
images:
- name: myapp
newTag: "1.2.0" # Update thisThen apply:
kubectl apply -k overlays/devAdvanced Patching
JSON Patch
# overlays/prod/kustomization.yaml
patches:
- target:
kind: Deployment
name: myapp
patch: |-
- op: replace
path: /spec/replicas
value: 10
- op: add
path: /spec/template/spec/affinity
value:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: myapp
topologyKey: kubernetes.io/hostnameMerge Patch
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: myapp
patch: |-
[{"op": "replace", "path": "/spec/replicas", "value":10}]Variable Replacement
# overlays/prod/kustomization.yaml
vars:
- name: DATABASE_HOST
objref:
kind: ConfigMap
name: app-config
apiVersion: v1
fieldref:
fieldpath: data.database-host
replicas:
- name: myapp
count: 10Use in ConfigMap:
data:
database-connection-string: "postgresql://$(DATABASE_HOST)/mydb"CommonLabels and Annotations
# base/kustomization.yaml
commonLabels:
app: myapp
managed-by: kustomize
version: v1
commonAnnotations:
description: "My awesome app"
last-updated: "2024-01-15"
contact: "[email protected]"Automatically applies to all resources.
Generators
ConfigMap from file
configMapGenerator:
- name: app-config
files:
- configs/app.yaml
- configs/db.yamlConfigMap from envs
configMapGenerator:
- name: app-config
envs:
- config.envSecrets
secretGenerator:
- name: db-secret
envs:
- secret.env
type: OpaqueBest Practices
✅ One base, multiple overlays
- Keep base minimal and environment-agnostic
- All customization in overlays
✅ Use namePrefix to avoid conflicts
namePrefix: prod- # All resources get prod- prefix✅ Track in Git
git add base/ overlays/
git commit -m "Update myapp configuration"
git push✅ Use with GitOps tools
# ArgoCD Application
spec:
source:
repoURL: https://github.com/myorg/myapp
path: overlays/prod
kind: kustomize❌ Don't nest overlays
# Bad
overlays/
prod/
overlays/
east/