Kustomize — Environment-Specific Kubernetes Configurations
Why Kustomize Matters
Kustomize is a Kubernetes-native configuration management tool. It lets you customize raw Kubernetes manifests for different environments without using templates or modifying the original files. Unlike Helm (which uses Go templates), Kustomize uses pure YAML with a patch-based overlay system.
Why this matters for your career:
- Kustomize is built directly into kubectl (since v1.14) — no extra tool needed
- It is the standard for managing environment-specific Kubernetes configurations
- Overlay pattern is cleaner and more maintainable than template-based approaches
- Kustomize integrates with ArgoCD, Flux, and CI/CD pipelines natively
What Is Kustomize?
Kustomize introduces the concept of "bases" and "overlays":
- Base: The shared, common configuration that applies to all environments
- Overlay: Environment-specific patches that customize the base for dev, staging, or production
Directory Structure
k8s/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── namespace.yaml
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ ├── replica-patch.yaml
│ └── configmap-patch.yaml
├── staging/
│ ├── kustomization.yaml
│ ├── replica-patch.yaml
│ └── ingress-patch.yaml
└── production/
├── kustomization.yaml
├── replica-patch.yaml
├── resource-patch.yaml
└── hpa.yaml
Base Configuration
base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- deployment.yaml
- service.yaml
commonLabels:
app: my-app
managed-by: kustomize
namePrefix: myapp-
base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: app
image: my-app:latest
ports:
- containerPort: 3000
env:
- name: LOG_LEVEL
value: info
base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- port: 80
targetPort: 3000
type: ClusterIP
Overlay Configuration
overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: replica-patch.yaml
- path: configmap-patch.yaml
configMapGenerator:
- name: app-config
literals:
- NODE_ENV=development
- LOG_LEVEL=debug
- API_URL=http://localhost:3000
images:
- name: my-app
newTag: dev-latest
overlays/dev/replica-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- hpa.yaml
patches:
- path: replica-patch.yaml
- path: resource-patch.yaml
- target:
kind: Deployment
name: api
patch: |-
- op: replace
path: /spec/template/spec/containers/0/image
value: my-app:v1.2.3
configMapGenerator:
- name: app-config
literals:
- NODE_ENV=production
- LOG_LEVEL=info
- API_URL=https://api.myapp.com
secretGenerator:
- name: app-secrets
env: .env.prod
type: Opaque
images:
- name: my-app
newTag: v1.2.3
overlays/production/replica-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
overlays/production/resource-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
template:
spec:
containers:
- name: app
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
overlays/production/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Using Kustomize
# Build dev manifests
kubectl kustomize overlays/dev
# Build production manifests
kubectl kustomize overlays/production
# Apply to cluster
kubectl apply -k overlays/dev
kubectl apply -k overlays/production
# With ArgoCD
# In Application spec:
spec:
source:
repoURL: https://github.com/myorg/my-app-config.git
path: k8s/overlays/production
Kustomize Features
| Feature | Description | Example |
|---------|-------------|--------|
| patches | Strategic merge patches | Change replica count per environment |
| patchesJson6902 | JSON patch operations | Replace image tag |
| configMapGenerator | Generate ConfigMaps from literals/files | Environment-specific env vars |
| secretGenerator | Generate Secrets from files/env | DB passwords per environment |
| commonLabels | Add labels to all resources | app: my-app, env: production |
| commonAnnotations | Add annotations to all resources | commit-hash: abc123 |
| namePrefix | Add prefix to all resource names | prod-api vs dev-api |
| nameSuffix | Add suffix to all resource names | api-prod vs api-dev |
| images | Override image tags | my-app:v1.2.3 vs my-app:latest |
| namespace | Set namespace for all resources | my-app-prod |
| vars | Inject values into resource fields | Use ConfigMap data in Deployment |
Kustomize vs. Helm
| Aspect | Kustomize | Helm | |--------|-----------|------| | Approach | Patch-based overlays | Template-based charts | | Complexity | Low (pure YAML) | Medium-High (Go templates) | | Learning curve | Low | Medium | | Parameterization | Patches + generators | Values + templates | | Package management | ❌ No | ✅ Yes (charts, repositories) | | Rollback | Git revert | helm rollback | | Built into kubectl | ✅ Yes (since v1.14) | ❌ No | | Best for | Customizing existing manifests | Distributing packaged applications |
Use Kustomize for your own applications where you control the manifests. Use Helm for third-party applications (Redis, PostgreSQL, Prometheus) where you need to consume packaged charts.
Best Practices
| Practice | Reason |
|----------|--------|
| Keep base minimal | Only include what's common across all environments |
| Use patches for environment specifics | Don't modify base — overlay changes only |
| Use configMapGenerator for env vars | Environment-specific configuration |
| Use secretGenerator for secrets | Secure, environment-specific secrets |
| Use images directive for tags | Override image tags without patching deployment |
| Name resources uniquely per env | Use namePrefix or namespace to avoid conflicts |
| Keep overlays shallow | One level of overlay per environment |
| Validate with kubectl kustomize | Check output before applying |
Summary
Kustomize provides a clean, template-free approach to managing environment-specific Kubernetes configurations. Bases define common resources, overlays customize them for each environment. Kustomize is built into kubectl and integrates seamlessly with ArgoCD and Flux.
Key takeaways:
- Base: shared configuration for all environments
- Overlay: environment-specific patches and generators
- Patches customize resources without modifying base files
- configMapGenerator and secretGenerator create environment-specific config
- images directive overrides container image tags per environment
- Built into kubectl (kubectl kustomize, kubectl apply -k)
- Use Kustomize for your apps, Helm for third-party charts
- Integrates natively with ArgoCD and Flux GitOps
What's Next: Image Updater
The next chapter covers automated container image updates with ArgoCD Image Updater — automatically detecting new images and updating deployments.