Kustomize overlays let you manage environment-specific configurations without duplicating your base Kubernetes manifests.
Here’s a simple Kustomize setup for deploying an app to dev, staging, and prod environments, each with slightly different settings.
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:latest
ports:
- containerPort: 80
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
Now, let’s create overlays for dev, staging, and prod.
Development Overlay
For development, we want more replicas and a different image tag.
# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patchesStrategicMerge:
- deployment-dev.yaml
# overlays/dev/deployment-dev.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
template:
spec:
containers:
- name: my-app
image: nginx:1.21.6
When you run kustomize build overlays/dev, Kustomize takes the base and applies the deployment-dev.yaml patch. It merges the replicas and image fields.
Staging Overlay
Staging might have even more replicas, and we’ll add a ConfigMap.
# overlays/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patchesStrategicMerge:
- deployment-staging.yaml
# overlays/staging/deployment-staging.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 5
template:
spec:
containers:
- name: my-app
image: nginx:1.21.6
# overlays/staging/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
feature_flags: "true"
The kustomization.yaml in staging lists the base and then adds deployment-staging.yaml and configmap.yaml to the resources.
Production Overlay
Production needs the highest replica count and a specific image. We’ll also use a commonLabels directive.
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patchesStrategicMerge:
- deployment-prod.yaml
commonLabels:
environment: prod
# overlays/prod/deployment-prod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 10
template:
spec:
containers:
- name: my-app
image: nginx:1.21.6
The commonLabels are applied to all resources generated by this overlay.
Applying the Overlays
You can use kustomize build to see the output for each environment:
kustomize build overlays/dev
kustomize build overlays/staging
kustomize build overlays/prod
To apply to your cluster:
kustomize build overlays/dev | kubectl apply -f -
kustomize build overlays/staging | kubectl apply -f -
kustomize build overlays/prod | kubectl apply -f -
Kustomize’s core strength here is its ability to patch and augment existing resources. It doesn’t just replace; it intelligently merges changes. For instance, when deployment-dev.yaml specifies replicas: 3, Kustomize finds the Deployment in the base, locates the spec.replicas field, and updates it to 3. If a new field is added in the overlay (like configmap.yaml in staging), Kustomize simply adds it to the final set of resources.
The secret sauce is how Kustomize handles transformations. For patchesStrategicMerge, it uses a strategic merge patch, which is aware of Kubernetes object structures, allowing it to merge lists and maps intelligently. For simpler additions or global modifications, commonLabels or resources directives work directly. The bases field is crucial for inheriting from a common set of configurations, preventing DRY violations. Kustomize builds up the final manifest by starting with the base, then applying patches and additions from the overlay. It’s a declarative way to manage variations across environments without resorting to templating engines and their associated complexity.
What people often miss is the power of patchesJson6902. This allows for fine-grained, arbitrary modifications to JSON objects, going beyond what strategic merge patches can do. It’s particularly useful for complex fields or when you need to perform operations like removing an element from a list.