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.

Unlock Full Tutorial

This chapter is paid content. Join the project to unlock over 5000 words of deep analysis, including 10+ god-tier Prompts and real Source Code examples!