Introduction
Kustomize, natively integrated into kubectl since v1.14, revolutionizes Kubernetes manifest management in 2026. Unlike Helm, which relies on templates, Kustomize applies declarative transformations using pure YAML files, enabling pure GitOps. Imagine a common base for an Nginx deployment, overlaid for environments (dev/prod) without duplication or magic variables.
Why adopt it? It avoids templating errors, integrates seamlessly with ArgoCD/Flux, and handles strategic patches for post-build mutations. This advanced tutorial covers everything from basic structure to Secret generators, with 8 complete YAML examples. By the end, you'll deploy a modular, scalable microservices app on EKS/GKE/AKS clusters. Ideal for senior DevOps engineers managing 100+ manifests.
Prerequisites
- kubectl 1.28+ with built-in Kustomize (
kubectl kustomize) - Accessible Kubernetes cluster (Minikube for testing, or production cloud)
- Advanced knowledge: Deployments, ConfigMaps, Secrets, RBAC
- Git for versioning overlays
- Standalone
kustomizeCLI (optional, viabrew install kustomize)
Install and Verify Kustomize
# Standalone installation (macOS/Linux)
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/
# Check with integrated kubectl
kubectl kustomize version
# Basic test
mkdir test-kust && cd test-kust
echo 'apiVersion: v1
kind: ConfigMap
metadata:
name: test' > configmap.yaml
echo 'resources:
- configmap.yaml' > kustomization.yaml
kubectl kustomize .This script installs Kustomize v5+ and verifies its kubectl integration. The test creates a simple ConfigMap via kustomization.yaml, demonstrating the build without kubectl apply. Avoid outdated versions for alpha generators like StrategicMergePatch.
Basic Kustomization Structure
A Kustomize project relies on a kustomization.yaml file that lists resources and transformations. Create a base/ folder for common, reusable manifests referenced via bases:. Think of it like Lego: the base assembles the pieces, and overlays add the colors.
Create a Simple Nginx Base
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
namespace: default
namePrefix: app-
nameSuffix: -v1
commonLabels:
app.kubernetes.io/name: nginx-app
version: v1.0This base kustomization.yaml references an Nginx Deployment and Service, adds common labels, and applies name pre/suffixes. Run kubectl kustomize . to generate the transformed manifests. Pitfall: omitting apiVersion causes parsing errors in v1beta1+.
Base Manifests (Deployment + Service)
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: ClusterIPThese manifests form the base: Deployment with 2 Nginx replicas, ClusterIP Service. Kustomize assembles them without initial changes. Copy-paste ready; test with kubectl apply -k base/. Note: labels must match for the selector.
Implement Overlays for Environments
Overlays extend the base via bases: [../base]. Create overlays/dev/ and overlays/prod/ for dev/prod. Use patches to mutate replicas, images, etc.
Dev Overlay with Patches
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
patchesStrategicMerge:
- deployment-patch.yaml
images:
- name: nginx
newTag: 1.25-alpine
replicas:
- name: nginx-deployment
count: 1
namespace: devThis overlay points to the base, patches the Deployment (replicas/image), and targets the dev namespace. patchesStrategicMerge merges mergeable fields. Build with kubectl kustomize overlays/dev. Avoid patchesJson6902 unless dealing with non-mergeables like arrays.
Strategic Patch for Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
containers:
- name: nginx
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1This patch adds resources and a RollingUpdate strategy to the base Deployment. StrategicMerge merges without overwriting. Crucial for prod: maxUnavailable: 0 ensures zero-downtime. Test diffs with kustomize build --enable-helm . if needed.
ConfigMap and Secret Generators
Kustomize excels with generators to inject sensitive or env-specific values without manual base64. configMapGenerator and secretGenerator hash files for immutability.
Prod Overlay with Generators
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../../base
configMapGenerator:
- name: app-config
files:
- config.properties=app-config.properties
- database.properties=db.properties
secretGenerator:
- name: db-secret
files:
- db-pass.txt=db-password.txt
type: Opaque
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
template:
spec:
volumes:
- name: config-volume
configMap:
name: app-config
- name: secret-volume
secret:
secretName: db-secret
containers:
- name: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/config
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
images:
- name: nginx
newTag: 1.25
newName: nginx
replicas:
- name: nginx-deployment
count: 5
namespace: prodThis overlay generates ConfigMap/Secret from files, patches the Deployment for volumes/mounts. Hash suffixes ensure rebuilds on changes. Place app-config.properties and db-password.txt in the folder. Secures secrets without kubectl create secret.
Advanced Patches and PostBuild
JSON6902 patches for precise mutations (e.g., arrays), postBuild for vars like GitOps image SHAs.
JSON6902 Patch and Vars
apiVersion: apps/v1
$patch: replace
$operation: add
path: /spec/template/spec/containers/0/env
value:
- name: ENV
value: production
- name: DB_HOST
valueFrom:
secretKeyRef:
name: db-secret
key: host
---
# In kustomization.yaml, add:
# vars:
# - name: IMAGE_TAG
# objref:
# kind: Deployment
# name: nginx-deployment
# apiVersion: apps/v1
# fieldref:
# fieldpath: spec.template.spec.containers.[name=nginx].image
# patchesJson6902: [patch-json.yaml]
# postBuild:
# substitute:
# IMAGE_TAG: latest-sha
# Full postBuild example in kustomization.yaml:
postBuild:
substitute:
app-name: "nginx-prod"
sha: "abc123"JSON6902 adds env vars to the first container. vars and postBuild.substitute enable dynamic injection (e.g., CI/CD SHA). Add to prod kustomization.yaml. Powerful for ArgoCD sync waves.
Full Deployment via kubectl
# Directory structure:
# kustomize-app/
# ├── base/
# │ ├── kustomization.yaml
# │ ├── deployment.yaml
# │ └── service.yaml
# └── overlays/
# ├── dev/
# │ ├── kustomization.yaml
# │ └── deployment-patch.yaml
# └── prod/
# ├── kustomization.yaml
# ├── deployment-patch.yaml
# ├── patch-json.yaml
# ├── app-config.properties
# └── db-password.txt
# Build and dry-run
kubectl kustomize overlays/prod | kubectl apply --dry-run=client -f -
# Real deployment
kubectl apply -k overlays/prod
# Verify
kubectl get all -n prod
kubectl rollout status deployment/nginx-deployment -n prodThis script assumes the full directory structure, performs build/dry-run then apply. -k uses built-in Kustomize. rollout status waits for readiness. Perfect for CI pipelines like GitHub Actions/Jenkins.
Best Practices
- Directory structure:
base/+overlays/{env}/{team}/for multi-team scalability. - Hashed generators: Always use
secretGeneratorwithtype: Opaqueand literals/files. - Patch priority: StrategicMerge > JSON6902; test diffs with
k diff. - GitOps ready: Commit overlays, ignore sensitive files via
.gitignore+ SealedSecrets. - Helm fallback: Enable
enableHelm: truefor hybrid, but prioritize pure Kustomize.
Common Errors to Avoid
- Missing apiVersion v1beta1: Causes
no kind "Kustomization"; always specify. - Merge conflicts: Non-mergeable arrays break; use JSON6902 with
$patch: replace. - Plaintext secrets: Never commit; use
literal:or SOPS +sopsGeneratorplugin. - Missing namespace: Apply without
-nfails; force vianamespace:in kustomization.
Next Steps
- Official docs: Kustomize Book
- Advanced plugins: Helm, SOPS for secrets (kustomize-sops)
- GitOps: Integrate with ArgoCD or Flux.
- Expert training: Discover our Kubernetes & GitOps courses.