Skip to main content

Kustomize: Template-Free Configuration Hydration

Kustomize is a declarative configuration management tool that allows you to manage environment-specific customizations without templating (e.g., Helm's Go templates). It utilizes a Base + Overlay pattern to "hydrate" raw YAML into environment-ready manifests.


1. ARCHITECTURAL CORE CONCEPTS

Kustomize operates on the principle of Inheritance via the Filesystem.

1.1 The Base

The "Source of Truth." It contains the common, raw Kubernetes manifests (Deployments, Services, ConfigMaps) that represent the generic application state.

1.2 The Overlay

A specialized directory that references a Base. It contains Patches and Transformers that modify the base resources for a specific environment (Dev, Staging, Prod).

1.3 Hydration vs. Templating

  • Helm (Templating): Uses placeholders ({{ .Values.name }}) and generates YAML via string replacement.
  • Kustomize (Hydration): Starts with valid YAML, merges changes, and outputs valid YAML.
    • Architectural Benefit: You can always kubectl apply a Base directly; you cannot do that with an unrendered Helm chart.

2. THE KUSTOMIZATION SCHEMA

The engine is controlled by kustomization.yaml. Strict Naming Rule: The file must be lowercase kustomization.yaml. Kustomization.yml or capitalized variants will be ignored by kubectl.

deploy/
├── base/ # Generic application logic
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/ # Environment-specific logic
├── dev/
│ ├── kustomization.yaml
│ └── patches.yaml
└── prod/
├── kustomization.yaml
└── replicas-patch.yaml

3. TRANSFORMERS: GLOBAL MODIFICATIONS

Transformers are high-level mechanisms to apply changes across all resources in a kustomization set.

TransformerDescriptionInternal Impact
namespaceSets the namespace for all resources.Updates metadata.namespace and cross-references (ServiceAccount bindings).
namePrefixPrepends a string to all resource names.Updates names and all selectors/references.
commonLabelsInjects labels into all resources.Updates metadata and selectors (if includeSelectors: true).
imagesOverrides container image tags/registries.Replaces image: tag without manual spec edits.

Example: Production Overlay Transformer

# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base

namespace: prod-namespace
namePrefix: enterprise-

images:
- name: my-api-image
newName: 123456789.dkr.ecr.us-east-1.amazonaws.com/api
newTag: v2.4.5

replicas:
- name: enterprise-my-deployment
count: 10

4. GENERATORS: IMMUTABLE CONFIG PATTERN

Kustomize can generate ConfigMaps and Secrets. Crucially, it appends a Content Hash to the name (e.g., my-config-h9k2b5).

4.1 The Rollout Trigger

When you change the content of a file in a configMapGenerator, the hash changes. This creates a New ConfigMap. Kubernetes sees the Deployment's reference has changed and triggers a Rolling Update.

  • Bible Usage: This is the gold standard for ensuring Pods always have the latest config without manually managing recreate strategies.
configMapGenerator:
- name: app-settings
files:
- configs/ui.properties
literals:
- API_TIMEOUT=30s

5. PATCHES: SURGICAL YAML MODIFICATION

For deep modifications (probes, affinity, env vars) that Transformers cannot reach, use Patches.

5.1 Strategic Merge Patch (YAML-style)

Updates the resource by providing a partial YAML snippet. Kustomize merges it into the base.

5.2 JSON Patch (RFC 6902)

Standardized, precise operations (add, replace, remove).

Bible-Grade Patch Example (In-line v5+ Syntax):

patches:
- target:
group: apps
version: v1
kind: Deployment
name: my-app
patch: |-
# 1. Add Environment Variables
- op: add
path: /spec/template/spec/containers/0/env
value:
- name: NODE_ENV
value: production

# 2. Modify Resource Limits
- op: replace
path: /spec/template/spec/containers/0/resources
value:
limits:
cpu: "2"
memory: "4Gi"
requests:
cpu: "100m"
memory: "512Mi"

# 3. Inject Liveness Probe (if missing)
- op: add
path: /spec/template/spec/containers/0/livenessProbe
value:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15

6. REPLACEMENTS (The Modern "Vars")

The legacy vars system is deprecated. The modern way to share data between resources (e.g., injecting a Service name into an Ingress) is replacements.

replacements:
- source:
kind: Service
name: my-backend
fieldPath: metadata.name
targets:
- select:
kind: Ingress
fieldPaths:
- spec.rules.0.http.paths.0.backend.service.name

7. PRODUCTION CLI WORKFLOW

7.1 Hydration Verification (The Dry Run)

Always inspect the hydrated output before applying.

# Render the final YAML to stdout
kubectl kustomize overlays/prod

# Use a linter on the rendered output
kubectl kustomize overlays/prod | kube-linter lint

7.2 Deployment

The -k flag tells kubectl to look for a kustomization.yaml and run the hydration engine before sending to the API.

kubectl apply -k overlays/prod

7.3 Debugging the "Edit Fix"

If you are moving from old Kustomize versions (v3/v4) to v5+, your patchesJson6902 will be deprecated. Use the fixer:

kustomize edit fix

8. ARCHITECT'S COMPARISON SUMMARY

FeatureKustomizeHelm
LogicNone (Declarative)High (Go Templating, Loops, If/Else)
Deploymentkubectl apply -khelm install
ImmutabilityVery High (Base stays valid)Medium (Charts are brittle)
StateNone (Cluster is Truth)Secret-based releases (helm list)
Learning CurveLow (Pure YAML)High (Template debugging)

9. PRODUCTION PITFALLS & WARNINGS

  1. SubPath Update Failure: If you patch a file inside a ConfigMap that is mounted via subPath, Kustomize's hash rotation will create a new ConfigMap, but the Pod's subPath mount will not update.
    • Solution: Always mount the whole directory or restart the Pod.
  2. Naming Collisions: namePrefix can cause issues if you have multiple resources that end up with the same name after prefixing.
  3. Cross-Namespace References: namespace transformer is powerful but can break ClusterRoleBindings if you aren't careful with the subjects namespace field.
  4. Implicit Ordering: Kustomize processes resources in the order they appear in resources:. Place dependencies (like Namespaces) at the top.