What is Kustomize? A Configuration Management Tool for Kubernetes

A comprehensive guide to Kustomize usage and implementation

Featured image

Image Reference



Overview

This post explores Kustomize, a powerful tool for customizing Kubernetes manifests.


What is Kustomize?

Kustomize is a template-free configuration customization tool for Kubernetes resources.

It lets you customize raw, template-free YAML files for multiple environments, leaving the original files untouched and usable as-is.

Key Features of Kustomize

No Templates: Uses plain YAML files without templating languages
Overlay-Based: Generates variants from a common base
Declarative: Describes the desired state, not how to achieve it
Native Integration: Built into kubectl since v1.14
GitOps Friendly: Perfect for source-controlled configuration
No Duplication: Reuses common configurations with minimal changes


Why Kustomize?

The Configuration Challenge

Managing Kubernetes YAML manifests across multiple environments presents several challenges:

Common Approaches to Configuration Management:
  1. Copy & Paste: Duplicate files for each environment (error-prone, hard to maintain)
  2. Templates: Use templating engines like Helm (adds complexity with templating language)
  3. Overlays: Apply patches to base configurations (Kustomize's approach)

Kustomize vs Helm

Feature Kustomize Helm
Approach Overlay-based patching Template-based generation
Syntax Native Kubernetes YAML Go templates syntax
Learning Curve Lower (if familiar with Kubernetes YAML) Higher (requires learning template syntax)
Package Management No central repository Charts repository system
Versioning Git-based versioning Chart versioning
Integration Native in kubectl Separate CLI tool
Best For Environment-specific variations with same resources Distributing/reusing complete applications


Installation

Installing Kustomize CLI

# Method 1: Direct download and install
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/

# Method 2: Using Homebrew (macOS)
brew install kustomize

# Method 3: Using Chocolatey (Windows)
choco install kustomize

Verifying Installation

kustomize version

Using Built-in kubectl Support

Since Kubernetes v1.14, Kustomize functionality is built into kubectl:

# Using kubectl with Kustomize
kubectl apply -k <directory>
kubectl diff -k <directory>
kubectl delete -k <directory>


Core Concepts

1. Base and Overlays

Kustomize follows a “base + overlay” model:

graph TD A[Base] --> B[Dev Overlay] A --> C[Staging Overlay] A --> D[Production Overlay] B --> E[Generated Dev Resources] C --> F[Generated Staging Resources] D --> G[Generated Production Resources] style A fill:#a5d6a7,stroke:#333,stroke-width:1px style B fill:#ffcc80,stroke:#333,stroke-width:1px style C fill:#ffcc80,stroke:#333,stroke-width:1px style D fill:#ffcc80,stroke:#333,stroke-width:1px style E fill:#e6ee9c,stroke:#333,stroke-width:1px style F fill:#e6ee9c,stroke:#333,stroke-width:1px style G fill:#e6ee9c,stroke:#333,stroke-width:1px

Typical Project Structure:

├── base
│   ├── kustomization.yaml     # Defines base resources
│   ├── deployment.yaml        # Common deployment configuration
│   ├── service.yaml           # Common service configuration  
│   └── configmap.yaml         # Common configmap
└── overlays
    ├── dev
    │   ├── kustomization.yaml # References base + dev patches
    │   └── patch-replicas.yaml # Dev-specific patches
    ├── staging
    │   ├── kustomization.yaml # References base + staging patches
    │   └── patch-replicas.yaml # Staging-specific patches
    └── production
        ├── kustomization.yaml # References base + production patches
        ├── patch-replicas.yaml # Production-specific patches
        └── patch-resources.yaml # Additional production patches

2. Kustomization File

The kustomization.yaml is the configuration file that tells Kustomize what resources to include and how to transform them.

Base Kustomization

# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
commonLabels:
  app: my-application

Overlay Kustomization

# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namePrefix: dev-
namespace: dev
commonLabels:
  environment: dev
  variant: development
patches:
- path: deployment-patch.yaml
# overlays/dev/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-application
spec:
  replicas: 2


Kustomize Transformers

Kustomize uses various transformers to modify resources. These are the key transformers available:

1. Name Transformers

# Add prefix to all resource names
namePrefix: dev-

# Add suffix to all resource names
nameSuffix: -v1

# Replace names
replacements:
- source:
    kind: ConfigMap
    name: source-name
  targets:
  - select:
      kind: Deployment
    fieldPaths:
    - spec.template.spec.volumes[0].configMap.name

2. Label Transformers

# Add labels to all resources
commonLabels:
  environment: dev
  app: my-application

# Add annotations to all resources
commonAnnotations:
  note: "This is a dev environment"
  version: "v1.0.0"

3. Patches

Strategic Merge Patches

Strategic merge patches use the Kubernetes resource structure to intelligently merge changes:

# kustomization.yaml
patches:
- path: patch-deployment.yaml
  target:
    kind: Deployment
# patch-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  replicas: 3  # Will override the replicas in base
  template:
    spec:
      containers:
      - name: nginx
        resources:  # Will be merged with existing resources
          limits:
            memory: "512Mi"

JSON 6902 Patches

For more precise changes, JSON 6902 patches can be used:

# kustomization.yaml
patchesJson6902:
- target:
    group: apps
    version: v1
    kind: Deployment
    name: my-deployment
  path: deployment-patch.yaml
# deployment-patch.yaml
- op: replace
  path: /spec/replicas
  value: 5

- op: add
  path: /metadata/annotations/monitoring
  value: "true"

4. ConfigMap and Secret Generators

# Generate ConfigMaps from files and literals
configMapGenerator:
- name: app-config
  files:
  - application.properties
  - settings.json
  literals:
  - ENVIRONMENT=dev
  - DEBUG=true

# Generate Secrets from files and literals
secretGenerator:
- name: app-secrets
  files:
  - secret.properties
  literals:
  - API_KEY=secret-value
  type: Opaque


Practical Implementation Guide

Let’s build a complete example of a microservice deployment with Kustomize.

Step 1: Create Base Structure

Create basic directory structure and files:

mkdir -p kustomize-demo/{base,overlays/{dev,staging,production}}

Step 2: Define Base Resources

Deployment

# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  selector:
    matchLabels:
      app: web-app
  replicas: 1
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web-app
        image: nginx:1.19
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"
        volumeMounts:
        - name: config-volume
          mountPath: /etc/nginx/conf.d
      volumes:
      - name: config-volume
        configMap:
          name: web-app-config

Service

# base/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-app
spec:
  selector:
    app: web-app
  ports:
  - port: 80
    targetPort: 80
  type: ClusterIP

ConfigMap

# base/nginx-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: web-app-config
data:
  default.conf: |
    server {
      listen 80;
      location / {
        root /usr/share/nginx/html;
        index index.html;
      }
    }

Base Kustomization

# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- nginx-config.yaml
commonLabels:
  app: web-app

Step 3: Create Overlays

Dev Environment

# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namePrefix: dev-
namespace: dev
commonLabels:
  environment: dev
  variant: development
patches:
- path: deployment-patch.yaml
# overlays/dev/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2

Staging Environment

# overlays/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namePrefix: staging-
namespace: staging
commonLabels:
  environment: staging
  variant: pre-production
patches:
- path: deployment-patch.yaml
# overlays/staging/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: web-app
        resources:
          limits:
            memory: "256Mi"
            cpu: "300m"

Production Environment

# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namePrefix: prod-
namespace: production
commonLabels:
  environment: production
  variant: stable
patches:
- path: deployment-patch.yaml
- path: service-patch.yaml
# overlays/production/deployment-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 5
  template:
    spec:
      containers:
      - name: web-app
        image: nginx:1.19-alpine
        resources:
          requests:
            memory: "128Mi"
            cpu: "200m"
          limits:
            memory: "512Mi"
            cpu: "500m"
# overlays/production/service-patch.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-app
spec:
  type: LoadBalancer

Step 4: Building and Applying

Build Manifests

# Generate manifests for different environments
kustomize build overlays/dev > dev-manifests.yaml
kustomize build overlays/staging > staging-manifests.yaml
kustomize build overlays/production > production-manifests.yaml

Apply to Cluster

# Using kustomize directly
kustomize build overlays/dev | kubectl apply -f -

# Using kubectl's built-in support
kubectl apply -k overlays/dev/


Advanced Features and Techniques

Component Reuse

Components are reusable pieces of kustomization that can be included in different bases or overlays:

# components/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- prometheus.yaml
- grafana.yaml

# To use the component:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
components:
- ../../components/monitoring

Composition and Inheritance

Kustomize supports multiple levels of composition:

graph TD A[Platform Base] --> B[Service Base] B --> C[Dev Overlay] B --> D[Staging Overlay] B --> E[Production Overlay] style A fill:#a5d6a7,stroke:#333,stroke-width:1px style B fill:#64b5f6,stroke:#333,stroke-width:1px style C fill:#ffcc80,stroke:#333,stroke-width:1px style D fill:#ffcc80,stroke:#333,stroke-width:1px style E fill:#ffcc80,stroke:#333,stroke-width:1px
# platform-base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- network-policy.yaml

# service-base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../platform-base
- deployment.yaml
- service.yaml

Custom Resource Transformers

Advanced transformations can be achieved with custom transformers:

transformers:
- mytransformer.yaml

# mytransformer.yaml
apiVersion: builtin
kind: PatchTransformer
metadata:
  name: custom-transformer
patch: |
  - op: add
    path: /spec/template/spec/containers/0/env/-
    value: 
      name: CUSTOM_ENV
      value: custom-value
target:
  kind: Deployment


Integrating Kustomize with CI/CD

GitOps Workflow

A GitOps approach to Kustomize typically involves:

  1. Source Control: Maintain base and overlays in Git
  2. Automated Builds: CI pipeline generates environment-specific manifests
  3. Deployment: CD system applies manifests to clusters
graph LR A[Git Repository] --> B[CI Pipeline] B --> C[Generated Manifests] C --> D[CD System] D --> E[Dev Cluster] D --> F[Staging Cluster] D --> G[Production Cluster] style A fill:#a5d6a7,stroke:#333,stroke-width:1px style B fill:#64b5f6,stroke:#333,stroke-width:1px style C fill:#ffcc80,stroke:#333,stroke-width:1px style D fill:#e6ee9c,stroke:#333,stroke-width:1px style E fill:#ce93d8,stroke:#333,stroke-width:1px style F fill:#ce93d8,stroke:#333,stroke-width:1px style G fill:#ce93d8,stroke:#333,stroke-width:1px

Example: GitLab CI Pipeline

# .gitlab-ci.yml
stages:
  - validate
  - build
  - deploy

validate:
  stage: validate
  script:
    - kustomize build overlays/dev | kubeval --strict
    - kustomize build overlays/staging | kubeval --strict
    - kustomize build overlays/production | kubeval --strict

build-manifests:
  stage: build
  script:
    - kustomize build overlays/dev > build/dev-manifests.yaml
    - kustomize build overlays/staging > build/staging-manifests.yaml
    - kustomize build overlays/production > build/production-manifests.yaml
  artifacts:
    paths:
      - build/

deploy-dev:
  stage: deploy
  environment: dev
  script:
    - kubectl apply -f build/dev-manifests.yaml
  only:
    - develop

deploy-staging:
  stage: deploy
  environment: staging
  script:
    - kubectl apply -f build/staging-manifests.yaml
  only:
    - main

deploy-production:
  stage: deploy
  environment: production
  script:
    - kubectl apply -f build/production-manifests.yaml
  only:
    - tags
  when: manual

Example: ArgoCD Integration

ArgoCD can directly use Kustomize to deploy applications:

# Application CRD for ArgoCD
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-application-dev
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/myorg/my-kustomize-repo.git
    targetRevision: HEAD
    path: overlays/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true


Common Patterns and Best Practices

⚙️ Best Practices
  • Directory Structure
    • Keep base configurations minimal and environment-agnostic
    • Use meaningful names for overlay directories
    • Group related components together
    • Consider using components for cross-cutting concerns
  • Version Control
    • Commit base configurations and overlays together
    • Document environment-specific requirements
    • Use tags for stable versions
    • Consider feature branches for major changes
  • Resource Management
    • Use namePrefix or nameSuffix to avoid naming conflicts
    • Implement proper namespace management
    • Use commonLabels for resource tracking
    • Generate configmaps and secrets instead of hardcoding
  • Security Considerations
    • Never commit sensitive data in base configurations
    • Use secretGenerator for sensitive information
    • Consider using Sealed Secrets or external secret management
    • Implement proper RBAC configurations

Real-World Example: Multi-Environment Microservice Deployment

Case Study: E-commerce Platform

An e-commerce company uses Kustomize to manage their 20+ microservices across development, staging, and production environments.

Implementation:

  • Platform base with common policies, RBAC, and network rules
  • Service-specific bases with standard configurations
  • Environment overlays customizing resources and replica counts
  • Region-specific overlays for global deployment
  • ArgoCD integration for GitOps-based continuous delivery

Benefits:

  • 90% reduction in duplicate configuration
  • Consistent environments with environment-specific tuning
  • Faster onboarding of new developers
  • Audit trail of all configuration changes
  • Reliable continuous deployment


Troubleshooting

Common Issues and Solutions

1. Resource Not Found

# Verify the resource exists in base
kubectl kustomize base

# Check overlay configuration
kubectl kustomize overlays/dev

Solution: Ensure file paths in resources sections are correct and verify file existence.

2. Patch Not Applied

# Debug patch application
kustomize build --load-restrictor LoadRestrictionsNone overlays/dev

# Verify patch syntax
kustomize build overlays/dev --enable-alpha-plugins

Solution: Check that target selectors match resource names exactly, including any name prefixes/suffixes.

3. Namespace Issues

# Verify namespace configuration
kubectl config get-contexts
kubectl config set-context --current --namespace=target-namespace

Solution: Ensure namespace is defined in kustomization.yaml if needed.

4. Version Compatibility

# Check Kustomize version
kustomize version

# Check kubectl version
kubectl version

Solution: Update Kustomize or kubectl to compatible versions.


Advanced Features

1. Strategic Merge Patches vs JSON Patches

# Strategic Merge Patch (understands Kubernetes semantics)
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: my-deployment
  spec:
    template:
      spec:
        containers:
        - name: nginx
          resources:
            limits:
              memory: 512Mi

# JSON 6902 Patch (more precise control)
patchesJson6902:
- target:
    version: v1
    kind: Deployment
    name: my-deployment
  path: patch.yaml

2. Cross-Cutting Fields with Transformers

# Transformer to add fields to all resources
apiVersion: builtin
kind: PatchTransformer
metadata:
  name: global-settings
patch: |-
  - op: add
    path: /metadata/labels/release-id
    value: v2023-01
target:
  labelSelector: app

3. Composition with Components

# Component for monitoring
components:
- ../../components/monitoring
- ../../components/logging


Conclusion and Takeaways

Key Benefits of Kustomize

When to Choose Kustomize



References