In this episode, we'll discuss Kustomize for customizing Kubernetes manifests without templating. We'll learn how to use overlays, patches, bases, and best practices for managing multiple environments.

Note
If you want to read the previous episode, you can click the Episode 38 thumbnail below
In the previous episode, we explored Helm Charts, which simplify packaging and deploying Kubernetes applications. Now we'll dive into Kustomize, which provides an alternative approach to customizing Kubernetes manifests.
Note: Here I'll be using a Kubernetes Cluster installed through K3s.
Kustomize is a native Kubernetes tool for customizing YAML manifests. Unlike Helm, Kustomize doesn't use templating. Instead, it uses a declarative approach with overlays and patches. Think of Kustomize like Git for Kubernetes manifests - it lets you compose, customize, and manage multiple versions of your configurations without template syntax.
Kustomize allows you to customize Kubernetes manifests by composing base configurations with overlays. It's built into kubectl, so you don't need to install anything extra.
1. No Templating Language
Use plain YAML without template syntax.
2. Declarative Approach
Describe what you want, not how to get it.
3. Reusability
Share base configurations across projects.
4. Multiple Environments
Easily manage dev, staging, and production.
5. Built-in to kubectl
No additional tools needed.
6. Git-Friendly
Works well with version control.
A typical Kustomize project has this structure:
my-app/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── configmap.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── patch-replicas.yaml
├── staging/
│ ├── kustomization.yaml
│ └── patch-replicas.yaml
└── production/
├── kustomization.yaml
├── patch-replicas.yaml
└── patch-resources.yamlThe base directory contains the common Kubernetes manifests:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
app: my-app
version: v1
commonAnnotations:
managed-by: kustomizeapiVersion: 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: app
image: myapp:1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512MiapiVersion: v1
kind: Service
metadata:
name: my-app
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 8080
selector:
app: my-appOverlays customize the base configuration for specific environments:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
replicas:
- name: my-app
count: 1
patchesStrategicMerge:
- patch-replicas.yaml
commonLabels:
environment: devapiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
replicas:
- name: my-app
count: 3
patchesStrategicMerge:
- patch-replicas.yaml
- patch-resources.yaml
commonLabels:
environment: production
images:
- name: myapp
newTag: "2.0"apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gikustomize build overlays/devOutputs the customized YAML.
kubectl apply -k overlays/devApplies the customized manifests to the cluster.
kubectl apply -k overlays/dev --dry-run=client -o yamlShows what would be applied without actually deploying.
kubectl diff -k overlays/devShows differences between current and desired state.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
commonLabels:
app: my-app
version: v1
managed-by: kustomize
commonAnnotations:
description: "My application"
team: platformapiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
replicas:
- name: my-app
count: 3
- name: worker
count: 5apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: myapp
newName: myregistry.azurecr.io/myapp
newTag: "2.0"
- name: worker
newTag: "1.5"Strategic Merge Patch
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patchesStrategicMerge:
- patch-deployment.yamlJSON Patch
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patchesJson6902:
- target:
group: apps
version: v1
kind: Deployment
name: my-app
patch: |-
- op: replace
path: /spec/replicas
value: 3apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: prod-
nameSuffix: -v1
resources:
- deployment.yamlThis creates resources named prod-my-app-v1.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
- deployment.yamlapiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=info
- DATABASE_HOST=db.example.com
files:
- config.yaml
secretGenerator:
- name: app-secret
literals:
- DATABASE_PASSWORD=secret123
files:
- .envapiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
commonLabels:
app: myappapiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: dev
replicas:
- name: myapp
count: 1
images:
- name: myapp
newTag: "dev"
commonLabels:
environment: devapiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
namespace: production
replicas:
- name: myapp
count: 3
images:
- name: myapp
newTag: "v1.0.0"
patchesStrategicMerge:
- patch-resources.yaml
commonLabels:
environment: productionapiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: app
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2GiProblem: Overlays duplicate base configuration instead of patching.
# DON'T DO THIS - Duplicating entire deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
# ... entire spec duplicatedSolution: Use patches instead:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3Problem: Each overlay is independent and duplicates configuration.
Solution: Always use bases for common configuration:
bases:
- ../../baseProblem: Patches don't apply correctly.
# DON'T DO THIS - Incomplete patch
spec:
replicas: 3Solution: Include full metadata:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3Problem: Using template syntax defeats the purpose of Kustomize.
Solution: Use pure YAML with Kustomize features.
Problem: Kustomization builds fail in production.
Solution: Always test before deploying:
kustomize build overlays/production
kubectl apply -k overlays/production --dry-run=clientoverlays/
├── dev/
├── staging/
└── production/namePrefix: prod-
nameSuffix: -v1Base should contain only common configuration.
patchesStrategicMerge:
- patch-replicas.yaml
- patch-resources.yamlAdd comments explaining what each overlay does:
# Production overlay
# - 3 replicas for high availability
# - Production image tag
# - Higher resource limitsKeep Kustomize files in Git:
git add base/ overlays/
git commit -m "Update kustomization for v2.0"configMapGenerator:
- name: app-config
literals:
- LOG_LEVEL=info| Aspect | Kustomize | Helm |
|---|---|---|
| Templating | No | Yes |
| Learning Curve | Easier | Steeper |
| Flexibility | Good | Excellent |
| Package Management | No | Yes |
| Built-in to kubectl | Yes | No |
| Community Charts | No | Yes |
| Use Case | Configuration Management | Package Management |
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
vars:
- name: REPLICAS
objref:
kind: Deployment
name: my-app
apiVersion: apps/v1
fieldref:
fieldpath: spec.replicasapiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- ../other-app/baseapiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
- ../../commonIn episode 39, we've explored Kustomize in Kubernetes in depth. We've learned how to customize Kubernetes manifests using overlays, patches, and bases without templating.
Key takeaways:
Kustomize provides a simpler alternative to Helm for managing Kubernetes configurations across multiple environments.
Note
If you want to continue to the next episode, you can click the Episode 40 thumbnail below