Kustomize lets you manage Kubernetes configurations without templating, using a declarative overlay system that’s surprisingly powerful.
Let’s see Kustomize in action. Imagine we have a base Kubernetes deployment for an Nginx server.
base/deployment.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:1.25.0
ports:
- containerPort: 80
Now, we want to deploy this to a "staging" environment. Instead of creating a whole new file, we’ll create a kustomization.yaml in a overlays/staging directory.
overlays/staging/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- deployment-patch.yaml
This kustomization.yaml tells Kustomize to use the base directory as its foundation and apply a patch defined in deployment-patch.yaml.
overlays/staging/deployment-patch.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
template:
spec:
containers:
- name: nginx
image: nginx:1.25.0-alpine
This patch increases the replica count to 2 and uses a different Nginx image tag for staging.
To build the final Kubernetes manifests for staging, you’d run:
kustomize build overlays/staging
This command outputs the merged YAML to your terminal. You’ll see the replicas: 2 and the updated image: nginx:1.25.0-alpine.
To apply these manifests directly to your Kubernetes cluster:
kustomize build overlays/staging | kubectl apply -f -
This pipeline is the core of Kustomize: build the final configuration, then pipe it to kubectl apply.
The mental model is one of composition. You define a base set of resources, and then overlays modify or add to that base for different environments or purposes. Kustomize doesn’t use templating languages like Go templating or Helm’s Tiller; it manipulates YAML structures directly. This means you’re always working with valid Kubernetes YAML, and the transformations are predictable.
You can also add completely new resources in an overlay. For instance, to add a Service to expose the Nginx deployment in staging:
overlays/staging/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
And then add service.yaml to the resources list in overlays/staging/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- service.yaml
patchesStrategicMerge:
- deployment-patch.yaml
Running kustomize build overlays/staging will now include both the patched Deployment and the new Service.
The one thing most people don’t realize is that Kustomize can also generate resources. For example, you can use a generatorOptions block with configMapGenerator or secretGenerator to create ConfigMaps or Secrets from literal values or files, and these generated resources are then merged into the final output. This allows you to inject environment-specific data without needing separate files for every overlay.
For example, to create a ConfigMap with a specific value for staging:
overlays/staging/kustomization.yaml (add this block):
configMapGenerator:
- name: app-config
literals:
- ENVIRONMENT=staging
- LOG_LEVEL=INFO
When you build this, Kustomize will create a ConfigMap named app-config with those key-value pairs, and this ConfigMap will be part of the generated output.
The next step is often managing multiple overlays and how they can inherit from each other, creating a hierarchy of configurations.