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.

Want structured learning?

Take the full Kustomize course →