Skip to content

11.09 Overlays

Kustomize overlays let you reuse one shared Kubernetes base and apply environment-specific changes for dev, stg, and prod without duplicating full YAML files.

Goal

Keep common manifests in base/, then use overlays to patch or extend them per environment.


Why Overlays?

Without overlays, teams often copy the same YAML files into separate folders:

dev/nginx.yml
stg/nginx.yml
prod/nginx.yml

That works for small labs, but it becomes hard to maintain in production because every change must be copied across environments.

Problem with duplicated YAML

If a new service, label, security setting, probe, or resource limit is added in one environment but forgotten in another, your environments drift.

Kustomize solves this by using:

base + overlay = final manifest

Base vs Overlay

Part Purpose Example
base/ Shared/default Kubernetes configs Deployment, Service, Redis deployment
overlays/dev/ Development-specific changes Smaller replica count, debug ConfigMap
overlays/stg/ Staging-specific changes Medium replica count
overlays/prod/ Production-specific changes Higher replicas, Grafana, stricter config

Note

The base is not directly tied to one environment. It is the reusable default configuration.


k8s/
├── base/
│   ├── kustomization.yaml
│   ├── nginx-depl.yaml
│   ├── service.yaml
│   └── redis-depl.yaml
└── overlays/
    ├── dev/
    │   ├── kustomization.yaml
    │   └── config-map.yaml
    ├── stg/
    │   ├── kustomization.yaml
    │   └── config-map.yaml
    └── prod/
        ├── kustomization.yaml
        ├── config-map.yaml
        └── grafana-depl.yaml

Tip

Put shared/default resources in base/. Put environment-specific patches or extra resources in each overlay folder.


Base kustomization.yaml

The base kustomization.yaml lists shared resources.

k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - nginx-depl.yaml
  - service.yaml
  - redis-depl.yaml

Example base deployment:

k8s/base/nginx-depl.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx

Note

The base can define default values, such as replicas: 1. Overlays can replace only the values that differ.


Overlay kustomization.yaml

An overlay imports the base using a relative path, then applies patches.

Newer Kustomize syntax

Older examples may use bases:. Modern Kustomize recommends using resources: to reference the base directory.

k8s/overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base
  - config-map.yaml

patches:
  - target:
      kind: Deployment
      name: nginx-deployment
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 2
k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base
  - config-map.yaml
  - grafana-depl.yaml

patches:
  - target:
      kind: Deployment
      name: nginx-deployment
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 3

Relative path

From k8s/overlays/dev/kustomization.yaml, the path ../../base means:

dev/ → overlays/ → k8s/ → base/

Adding Extra Resources in an Overlay

Overlays are not limited to patches. They can also add new resources that do not exist in the base.

Example: add Grafana only in production.

k8s/
└── overlays/
    └── prod/
        ├── kustomization.yaml
        ├── config-map.yaml
        └── grafana-depl.yaml
k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base
  - config-map.yaml
  - grafana-depl.yaml

patches:
  - target:
      kind: Deployment
      name: nginx-deployment
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 3

Success

This keeps Grafana production-only while the base remains clean and reusable.


Feature-Based Overlay Structure

For larger applications, you can organize overlays by feature or component.

k8s/
├── base/
│   ├── db/
│   │   ├── db-depl.yaml
│   │   ├── db-service.yaml
│   │   └── kustomization.yaml
│   ├── api/
│   │   ├── api-depl.yaml
│   │   ├── api-service.yaml
│   │   └── kustomization.yaml
│   └── kustomization.yaml
└── overlays/
    ├── dev/
    │   ├── db/
    │   │   ├── db-patch.yaml
    │   │   └── kustomization.yaml
    │   ├── api/
    │   │   ├── api-patch.yaml
    │   │   └── kustomization.yaml
    │   └── kustomization.yaml
    └── prod/
        ├── db/
        │   ├── db-patch.yaml
        │   └── kustomization.yaml
        ├── api/
        │   ├── api-patch.yaml
        │   └── kustomization.yaml
        └── kustomization.yaml

Tip

Your base and overlay subdirectories do not have to match exactly, but they must import the correct resources through kustomization.yaml.


Build and Apply

kustomize build k8s/overlays/dev
kustomize build k8s/overlays/prod
kubectl apply -k k8s/overlays/dev
kubectl apply -k k8s/overlays/prod
kubectl delete -k k8s/overlays/dev
kubectl delete -k k8s/overlays/prod

Note

kustomize build only renders YAML. It does not deploy anything unless you pipe it to kubectl apply -f - or use kubectl apply -k.


Common Overlay Use Cases

Use Case Dev Staging Production
Replicas 1 or 2 2 or 3 3+
Resource limits Low Medium Strictly defined
ConfigMaps Debug config Pre-prod config Production config
Extra tools Optional Optional Monitoring/logging
Namespaces dev stg prod
Image tags Fast-moving Release candidate Stable version

Example

A production overlay may add Grafana, stricter resource limits, higher replicas, and production-only ConfigMaps.


Production Best Practices

Do

  • Keep base/ small, reusable, and environment-neutral.
  • Use overlays for environment-specific changes only.
  • Use separate overlays for dev, stg, and prod.
  • Validate output with kustomize build before applying.
  • Commit rendered changes through pull requests when using GitOps.
  • Keep patches focused and easy to review.
  • Use clear folder names such as overlays/dev, overlays/stg, and overlays/prod.

Don't

  • Do not duplicate full YAML files across environments.
  • Do not put production-only resources in the base.
  • Do not hardcode secrets in overlay YAML files.
  • Do not mix unrelated changes in one large patch.
  • Do not apply overlays without reviewing the final rendered output.
  • Do not let environment overlays drift without review.

Common mistake

Adding a patch in an overlay but forgetting to import the base with ../../base means Kustomize has nothing to patch.


Do and Don't Summary

Do Don't
Use base/ for shared defaults Copy the same YAML into every environment
Use overlays for environment-specific config Edit base directly for production-only needs
Preview with kustomize build Apply blindly
Add extra prod-only resources in overlays/prod Put prod-only tools into base
Keep patches small Create huge unreadable patch files

Quick Troubleshooting

Why did my patch not apply?

Check that the target.name, target.kind, and resource name match exactly.

Why is Kustomize unable to find the base?

Verify the relative path from the overlay file. From k8s/overlays/dev, the base path is usually:

resources:
  - ../../base

Why did a resource appear only in prod?

It is likely listed only in overlays/prod/kustomization.yaml, such as:

resources:
  - grafana-depl.yaml

Avoid environment drift

If dev, stg, and prod behave differently, confirm whether the difference is intentional and documented in the overlay.


Final Mental Model

Quote

Base is the common starting point. Overlay is the environment-specific change layer. Kustomize combines both and produces the final Kubernetes manifest.

base/
  shared defaults

overlays/dev/
  dev patches + dev-only resources

overlays/stg/
  staging patches + staging-only resources

overlays/prod/
  production patches + production-only resources
kubectl apply -k k8s/overlays/prod

Success

This gives you clean, reusable, production-friendly Kubernetes configuration management without copying YAML across environments.