iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Kustomize

Kustomize is the "no templates" way to manage Kubernetes manifests. You write plain YAML for a base, then overlays patch it per environment. Built into kubectl (`kubectl apply -k`), works alongside Helm, and avoids the "two-week onboarding into a templating language" cost of large Helm charts.

Base + overlays + patches + secret generators

EXAMPLE
# Layout
# k8s/
# ├── base/
# │   ├── kustomization.yaml
# │   ├── deployment.yaml
# │   ├── service.yaml
# │   └── ingress.yaml
# └── overlays/
#     ├── staging/
#     │   ├── kustomization.yaml
#     │   └── patches/
#     │       └── replicas.yaml
#     └── production/
#         ├── kustomization.yaml
#         ├── patches/
#         │   ├── replicas.yaml
#         │   └── resources.yaml
#         └── ingress-host.yaml

# ===== base/deployment.yaml =====
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shop-api
  labels: { app: shop-api }
spec:
  replicas: 1
  selector: { matchLabels: { app: shop-api } }
  template:
    metadata: { labels: { app: shop-api } }
    spec:
      containers:
        - name: api
          image: example.com/shop-api:0.0.0    # overlays set the real tag
          ports: [{ containerPort: 8080 }]
          env:
            - { name: NODE_ENV, value: development }
          resources:
            requests: { cpu: '100m', memory: '256Mi' }
            limits:   { cpu: '500m', memory: '512Mi' }

# ===== base/service.yaml =====
apiVersion: v1
kind: Service
metadata: { name: shop-api }
spec:
  selector: { app: shop-api }
  ports: [{ port: 80, targetPort: 8080 }]

# ===== base/kustomization.yaml =====
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shop
resources:
  - deployment.yaml
  - service.yaml
  - ingress.yaml
commonLabels:
  app: shop-api
images:
  - name: example.com/shop-api
    newTag: latest

# ===== overlays/staging/kustomization.yaml =====
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shop-staging
resources:
  - ../../base
images:
  - name: example.com/shop-api
    newTag: 'staging-1.4.0'
patches:
  - path: patches/replicas.yaml
configMapGenerator:
  - name: shop-config
    literals:
      - NODE_ENV=staging
      - API_BASE=https://api.staging.example.com
secretGenerator:
  - name: shop-secrets
    envs: [ .env.staging ]      # .env.staging is gitignored
generatorOptions:
  disableNameSuffixHash: false   # name gets a hash so new contents trigger rolling update

# patches/replicas.yaml — patch only specific fields
apiVersion: apps/v1
kind: Deployment
metadata: { name: shop-api }
spec:
  replicas: 3

# ===== overlays/production/kustomization.yaml =====
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: shop-prod
resources:
  - ../../base
images:
  - name: example.com/shop-api
    newTag: '1.4.0'
patches:
  - path: patches/replicas.yaml
  - path: patches/resources.yaml
  - target: { kind: Ingress, name: shop-api }
    path: ingress-host.yaml
configMapGenerator:
  - name: shop-config
    literals:
      - NODE_ENV=production
      - API_BASE=https://api.example.com

# patches/resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: shop-api }
spec:
  template:
    spec:
      containers:
        - name: api
          resources:
            requests: { cpu: '200m', memory: '512Mi' }
            limits:   { cpu: '1000m', memory: '1Gi' }

# ===== Use it =====
# Render to stdout
kubectl kustomize k8s/overlays/staging

# Apply (kubectl 1.21+ has kustomize built in)
kubectl apply -k k8s/overlays/staging
kubectl apply -k k8s/overlays/production

# Diff before apply
kubectl diff -k k8s/overlays/production

# Delete
kubectl delete -k k8s/overlays/staging

# ===== Patterns to internalise =====
# - Base is the cluster-agnostic shape; overlays customise
# - Generators (configMap, secret) hash the contents into the name -> rolling
#   update happens when the content changes
# - 'images:' is the cleanest way to set the tag per environment
# - patches override fields surgically; replace fields entirely with JSON 6902
#   patches when needed
# - Use components for reusable bits (e.g. 'add-istio-sidecar')

# ===== Pitfalls =====
# - Editing the base from an overlay (do not — patch in the overlay)
# - Committing secrets to overlays (use envs: + gitignore the env file)
# - Forgetting to bump 'newTag' on deploy (overlays continue to point at old image)
# - Mixing kustomize with Helm in a way the team finds confusing
#   -> pick one: kustomize for your services, Helm for off-the-shelf charts

# ===== When to pick kustomize vs helm =====
# Kustomize: your own services where YAML is the cleanest expression
# Helm:      off-the-shelf 3rd-party services (Redis, Cert-Manager, NGINX)
# Combined:  use Helm to install the operator, kustomize to manage your CRs

# ===== Tooling =====
# kustomize edit set image example.com/shop-api=:1.4.1     # CLI mutation
# kustomize build --enable-helm  # mix Helm subcharts
# Skaffold / ArgoCD support kustomize natively

Why it matters

Pair kustomize for your services with Helm for off-the-shelf dependencies. Helm wins where you want a vendor-maintained chart with rich values; kustomize wins for the manifests you own, because the diff between staging and production is a tiny overlay you can read in seconds — no templating language to learn.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
# Base + overlays. Same YAML, env-specific patches.
kubectl apply -k overlays/prod
Try it Yourself »

Discussion

Loading…