Available for day contractsFrom 21st September I have availability for day and half day contracts. Please contact for more information.

Contact →
mikepreston.org

Kustomize

Kubernetes-native configuration management tool for customising application deployments without templating.

Kustomize Cheatsheet

Kubernetes-native configuration management tool for customising application deployments without templating.

Overview

Kustomize allows you to customise raw, template-free YAML files for multiple purposes, leaving the original YAML untouched and usable as-is. It uses a layered approach with bases and overlays to manage environment-specific configurations.

Environment OverlaysKustomize ArchitectureBase Resourceskustomization.yamlPatchesGeneratorsTransformersFinal Manifestsdev overlaystaging overlayproduction overlaykustomization.yamlkustomization.yamlkustomization.yamlEnvironment OverlaysKustomize ArchitectureBase Resourceskustomization.yamlPatchesGeneratorsTransformersFinal Manifestsdev overlaystaging overlayproduction overlaykustomization.yamlkustomization.yamlkustomization.yaml

Base and Overlay Structure

Key Concepts

  • Base: A directory containing a kustomization.yaml file with shared resources
  • Overlay: A directory that references and customises a base for specific environments
  • Resource: Any Kubernetes manifest file (Deployment, Service, ConfigMap, etc.)
  • Kustomization: The kustomization.yaml file that orchestrates all customisations

Directory Structure

Typical Project Structureproject/base/overlays/kustomization.yamldeployment.yamlservice.yamldev/staging/production/kustomization.yamlkustomization.yamlkustomization.yamlTypical Project Structureproject/base/overlays/kustomization.yamldeployment.yamlservice.yamldev/staging/production/kustomization.yamlkustomization.yamlkustomization.yaml

Examples

Base kustomization.yaml:

# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml
  - configmap.yaml

labels:
  - pairs:
      app: myapp

namespace: default

Base deployment.yaml:

# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "64Mi"
              cpu: "250m"
            limits:
              memory: "128Mi"
              cpu: "500m"

Development overlay:

# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

namespace: dev

namePrefix: dev-

labels:
  - pairs:
      environment: development

replicas:
  - name: myapp
    count: 1

images:
  - name: myapp
    newTag: dev-latest

Production overlay:

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

namespace: production

namePrefix: prod-

labels:
  - pairs:
      environment: production

replicas:
  - name: myapp
    count: 3

images:
  - name: myapp
    newTag: v1.2.3

patches:
  - path: increase-resources.yaml

Patching Strategies

Key Concepts

  • Strategic Merge Patch: Merges patches with original resources using Kubernetes strategic merge rules
  • JSON Patch (RFC 6902): Precise operations (add, remove, replace, move, copy, test)
  • JSON 6902 Patch: JSON patch with target selector for identifying resources
  • Inline Patches: Patches defined directly in kustomization.yaml

Patching Flow

Patch Application OrderOriginal ResourceStrategic MergePatchesJSON PatchesTransformersFinal ResourcePatch Application OrderOriginal ResourceStrategic MergePatchesJSON PatchesTransformersFinal Resource

Strategic Merge Patch Examples

Separate file patch:

# overlays/production/increase-resources.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  template:
    spec:
      containers:
        - name: myapp
          resources:
            requests:
              memory: "256Mi"
              cpu: "500m"
            limits:
              memory: "512Mi"
              cpu: "1000m"

Inline strategic merge patch:

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

patches:
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: myapp
      spec:
        replicas: 5
        template:
          spec:
            containers:
              - name: myapp
                env:
                  - name: LOG_LEVEL
                    value: "debug"

JSON Patch Examples

JSON 6902 patch with target:

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

patches:
  - target:
      group: apps
      version: v1
      kind: Deployment
      name: myapp
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 5
      - op: add
        path: /spec/template/spec/containers/0/env/-
        value:
          name: NEW_VAR
          value: "new-value"
      - op: remove
        path: /spec/template/spec/containers/0/resources/limits

External JSON patch file:

# kustomization.yaml
patches:
  - path: patches/deployment-patch.yaml
    target:
      kind: Deployment
      name: myapp
# patches/deployment-patch.yaml
- op: replace
  path: /spec/replicas
  value: 3
- op: add
  path: /metadata/annotations
  value:
    custom.io/managed-by: kustomize

Wildcard targeting:

# Apply to all Deployments
patches:
  - target:
      kind: Deployment
    patch: |-
      - op: add
        path: /metadata/annotations/managed-by
        value: kustomize

# Apply using label selector
patches:
  - target:
      kind: Deployment
      labelSelector: "app=myapp"
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 2

ConfigMap and Secret Generators

Key Concepts

  • Generators: Create ConfigMaps and Secrets from files, literals, or environment files
  • Hash Suffix: Automatically append content hash to names for rolling updates
  • Behaviour Options: Create, replace, or merge with existing resources

Examples

ConfigMap from literals:

# kustomization.yaml
configMapGenerator:
  - name: app-config
    literals:
      - DATABASE_HOST=localhost
      - DATABASE_PORT=5432
      - LOG_LEVEL=info

ConfigMap from files:

configMapGenerator:
  - name: app-config
    files:
      - config.properties
      - settings.json

  - name: nginx-config
    files:
      - nginx.conf=configs/custom-nginx.conf

ConfigMap from env file:

configMapGenerator:
  - name: app-env
    envs:
      - .env.production

Secret generators:

secretGenerator:
  - name: db-credentials
    literals:
      - username=admin
      - password=secretpassword
    type: Opaque

  - name: tls-secret
    files:
      - tls.crt
      - tls.key
    type: kubernetes.io/tls

  - name: docker-registry
    files:
      - .dockerconfigjson=docker-config.json
    type: kubernetes.io/dockerconfigjson

Generator options:

configMapGenerator:
  - name: app-config
    literals:
      - KEY=value
    options:
      disableNameSuffixHash: true
      labels:
        app: myapp
      annotations:
        note: "Generated by Kustomize"

generatorOptions:
  disableNameSuffixHash: false
  labels:
    generated: "true"
  annotations:
    generator: kustomize

Behaviour options:

configMapGenerator:
  - name: existing-config
    behavior: merge  # create | replace | merge
    literals:
      - NEW_KEY=new-value

Common Transformers

Key Concepts

  • Transformers: Modify resources globally across all manifests
  • Built-in Transformers: namePrefix, nameSuffix, namespace, labels, commonAnnotations (commonLabels is deprecated in kustomize v5 in favour of labels)
  • Image Transformer: Update container images without patching
  • Replica Transformer: Change replica counts

Examples

Name transformers:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

# Add prefix to all resource names
namePrefix: prod-

# Add suffix to all resource names
nameSuffix: -v2

Namespace transformer:

# Set namespace for all resources
namespace: production

Label and annotation transformers:

# Add labels to all resources (set includeSelectors: true to also
# inject into selectors — the old commonLabels behaviour)
labels:
  - pairs:
      app.kubernetes.io/name: myapp
      app.kubernetes.io/version: "1.0.0"
      environment: production

# Add annotations to all resources
commonAnnotations:
  owner: platform-team
  contact: platform@example.com

Image transformer:

images:
  # Change image tag
  - name: myapp
    newTag: v2.0.0

  # Change image name and tag
  - name: nginx
    newName: my-registry/nginx
    newTag: "1.21"

  # Use digest instead of tag
  - name: redis
    newName: redis
    digest: sha256:abc123def456...

Replica transformer:

replicas:
  - name: myapp-deployment
    count: 5
  - name: worker-deployment
    count: 3

Custom transformer configurations:

# kustomization.yaml
transformers:
  - label-transformer.yaml

# label-transformer.yaml
apiVersion: builtin
kind: LabelTransformer
metadata:
  name: custom-labels
labels:
  custom-label: custom-value
fieldSpecs:
  - path: metadata/labels
    create: true

Integration with kubectl

Common Commands

# Build and preview manifests
kustomize build overlays/production

# Apply using kustomize build
kustomize build overlays/production | kubectl apply -f -

# Apply using kubectl's built-in kustomize
kubectl apply -k overlays/production

# Preview what would be applied
kubectl apply -k overlays/production --dry-run=client -o yaml

# Delete resources
kubectl delete -k overlays/production

# Diff against current state
kubectl diff -k overlays/production

# View specific resource
kustomize build overlays/production | kubectl get -f - -o yaml

Advanced kubectl Integration

# Apply with server-side apply
kubectl apply -k overlays/production --server-side

# Apply with pruning (remove deleted resources)
kubectl apply -k overlays/production --prune -l app=myapp

# Create namespace if needed
kubectl create namespace production --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -k overlays/production

# Validate manifests
kustomize build overlays/production | kubectl apply --dry-run=server -f -

# Export to file
kustomize build overlays/production > production-manifests.yaml

# Use with specific kubeconfig
KUBECONFIG=~/.kube/prod-config kubectl apply -k overlays/production

CI/CD Integration

#!/bin/bash
# Example CI/CD script

# Validate kustomization
kustomize build overlays/$ENVIRONMENT > /dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "Kustomization build failed"
    exit 1
fi

# Check for drift
kubectl diff -k overlays/$ENVIRONMENT
DIFF_EXIT=$?

if [ $DIFF_EXIT -eq 0 ]; then
    echo "No changes to apply"
elif [ $DIFF_EXIT -eq 1 ]; then
    echo "Changes detected, applying..."
    kubectl apply -k overlays/$ENVIRONMENT
else
    echo "Error checking diff"
    exit 1
fi

Best Practices for Organisation

Key Concepts

  • DRY Principle: Keep common configurations in bases
  • Environment Parity: Minimise differences between environments
  • Version Control: Track all kustomization files in git
  • Validation: Always validate before applying

Recommended Directory Structure

project/
├── base/
│   ├── kustomization.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── configmap.yaml
├── components/
│   ├── monitoring/
│   │   ├── kustomization.yaml
│   │   └── service-monitor.yaml
│   ├── logging/
│   │   ├── kustomization.yaml
│   │   └── fluent-bit.yaml
│   └── security/
│       ├── kustomization.yaml
│       └── network-policy.yaml
├── overlays/
│   ├── dev/
│   │   ├── kustomization.yaml
│   │   └── patches/
│   ├── staging/
│   │   ├── kustomization.yaml
│   │   └── patches/
│   └── production/
│       ├── kustomization.yaml
│       ├── patches/
│       └── secrets/
└── README.md

Using Components

# components/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component

resources:
  - service-monitor.yaml

configMapGenerator:
  - name: prometheus-config
    files:
      - prometheus.yml
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ../../base

components:
  - ../../components/monitoring
  - ../../components/logging

Best Practice Examples

Use vars for cross-resource references (deprecated, use replacements):

# Modern approach with replacements
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml

replacements:
  - source:
      kind: Service
      name: myapp
      fieldPath: metadata.name
    targets:
      - select:
          kind: Deployment
          name: myapp
        fieldPaths:
          - spec.template.spec.containers.[name=myapp].env.[name=SERVICE_NAME].value

Organise patches by purpose:

# overlays/production/kustomization.yaml
patches:
  # Resource scaling
  - path: patches/replicas.yaml

  # Security configurations
  - path: patches/security-context.yaml

  # Environment-specific configs
  - path: patches/env-vars.yaml

Version pinning for remote bases:

resources:
  # Pin to specific version/tag
  - github.com/organisation/repo/base?ref=v1.2.3

  # Or specific commit
  - github.com/organisation/repo/base?ref=abc123

Quick Reference

Operation Command/Configuration Description
Build manifests kustomize build <dir> Generate final YAML
Apply with kubectl kubectl apply -k <dir> Apply kustomization
Preview changes kubectl diff -k <dir> Show differences
Delete resources kubectl delete -k <dir> Remove resources
Add resource resources: [file.yaml] Include manifest
Add patch patches: [{path: patch.yaml}] Apply modification
Set namespace namespace: <name> Override namespace
Add prefix namePrefix: <prefix>- Prefix all names
Add suffix nameSuffix: -<suffix> Suffix all names
Update image images: [{name: x, newTag: y}] Change image tag
Set replicas replicas: [{name: x, count: n}] Change replica count
Add labels labels: [{pairs: {key: value}}] Label all resources
Generate ConfigMap configMapGenerator: [...] Create ConfigMap
Generate Secret secretGenerator: [...] Create Secret
Include component components: [path/] Add reusable component
Remote base resources: [url?ref=tag] Use remote kustomization

Common Issues and Solutions

Issue: Hash suffix causing reference mismatches

Problem: ConfigMap/Secret name hash changes but dependent resources don't update.

Solution: Kustomize automatically updates references in the same kustomization. Ensure all dependent resources are in the same build.

# This works automatically
configMapGenerator:
  - name: app-config
    literals:
      - KEY=value

# References are updated automatically
# deployment.yaml
envFrom:
  - configMapRef:
      name: app-config  # Will be updated to app-config-<hash>

Issue: Patches not applying correctly

Problem: Strategic merge patch doesn't produce expected result.

Solution: Ensure correct API version and kind, and use proper merge keys.

# Correct: Include all required identifiers
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp  # Must match exactly
spec:
  template:
    spec:
      containers:
        - name: myapp  # Container name is the merge key
          resources:
            limits:
              memory: "512Mi"

Issue: Resources not found in base

Problem: error: accumulating resources: resource not found.

Solution: Verify relative paths and ensure files exist.

# Check path is correct relative to kustomization.yaml
resources:
  - ../../base  # Must exist and contain kustomization.yaml

Issue: Duplicate resources

Problem: may not add resource with an already registered id.

Solution: Ensure resources are only included once across bases and overlays.

# Wrong: Including same resource twice
resources:
  - ../../base
  - ../../base/deployment.yaml  # Already included via base

# Correct: Only include the base
resources:
  - ../../base

Issue: commonLabels breaking selector immutability

Problem: Applying the deprecated commonLabels to an existing Deployment fails because it also injects the labels into immutable selectors.

Solution: Use the labels field, which only touches selectors when you ask it to.

# Adds labels to metadata and pod templates without touching selectors
labels:
  - pairs:
      new-label: value
    includeTemplates: true
# (includeSelectors: true would reproduce the old commonLabels behaviour)

Issue: Secret values not being encoded

Problem: Secret generator not base64-encoding values.

Solution: Kustomize automatically encodes literals and file contents. Don't pre-encode.

secretGenerator:
  - name: my-secret
    literals:
      - password=plaintext  # Will be base64 encoded automatically

Issue: Remote bases not updating

Problem: Changes to remote base not reflected in builds.

Solution: Pin to a specific commit or tag and update the ref deliberately — kustomize fetches remote bases per build, so unexpected staleness usually means a floating ref resolved elsewhere (e.g. a CI mirror or proxy).

# Pin to specific version
resources:
  - github.com/org/repo/base?ref=v1.2.3

Issue: kubectl apply -k differs from kustomize build

Problem: Different versions produce different results.

Solution: Check kubectl's embedded kustomize version and consider using standalone kustomize.

# Check kubectl's kustomize version
kubectl version --client -o yaml | grep kustomizeVersion

# Use standalone kustomize for consistency
kustomize build overlays/prod | kubectl apply -f -

Related Topics

  • Helm: Kubernetes package manager with templating - complements Kustomize for chart-based deployments
  • ArgoCD: GitOps continuous delivery tool with native Kustomize support
  • Kubernetes RBAC: Role-based access control for securing deployments managed by Kustomize
  • GitOps Workflows: Best practices for managing infrastructure as code with Git
  • Kubernetes Secrets Management: Tools like Sealed Secrets or External Secrets for secure secret handling
  • Kubernetes Admission Controllers: Validating and mutating webhooks for policy enforcement on Kustomize outputs