Kustomize
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
Every earlier page of this section wrote plain YAML by hand. That works for one environment, but the moment the
same Bookshelf stack has to run in a dev and a prod cluster — with different replica counts, image tags,
hostnames and resource limits — copy-pasting manifests becomes a maintenance problem. Kustomize solves it without
a template language: you keep a valid, plain-YAML base and describe each environment as an overlay that patches
it. This page is written against Kubernetes 1.37 and Kustomize 5.8 (the version embedded in kubectl
1.37 may lag the standalone release, so check kubectl version — see
the section index for the full version baseline). Its templating-based
counterpart is Helm, and delivering either one continuously is the subject of
GitOps and CI/CD. Kustomize operates on the same objects described in
Objects and the API, and is applied with the commands from
kubectl Essentials.
Two ways to run it
Kustomize exists in two forms, and mixing them up is a common source of "it works on my machine":
-
Built into kubectl.
kubectl kustomize <dir>renders the result to standard output, andkubectl apply -k <dir>renders and applies it in one step. No extra install is needed, but the embedded Kustomize version is whatever thatkubectlwas built with, so the newest fields may be missing. -
Standalone
kustomize.kustomize build <dir>renders the same output using the latest release. It is what CI and GitOps tools typically invoke, so a manifest that builds in the standalone binary but not inkubectl -kis usually a version gap, not a bug.
# Render to stdout, nothing is sent to the cluster
kubectl kustomize k8s/overlays/dev
# Render and apply; -k means "the argument is a directory with a kustomization.yaml"
kubectl apply -k k8s/overlays/dev
# Standalone binary, same directory
kustomize build k8s/overlays/dev | kubectl apply -f -
# Compare live state with what would be applied
kubectl diff -k k8s/overlays/dev
Always run kubectl kustomize (or kubectl diff -k) before apply -k: the rendered output is the single source of
truth for what will actually reach the API server.
The kustomization.yaml fields
A directory becomes a Kustomize unit when it contains a file named kustomization.yaml. The fields you will use
most:
| Field | What it does |
|---|---|
|
Files, directories or remote bases to include. An overlay lists its base directory here. |
|
Sets |
|
Prepends or appends a string to every resource name, and rewrites references to those names (for example a
|
|
Adds labels to every resource; with |
|
Rewrites |
|
Overrides |
|
Builds a |
|
Strategic-merge or JSON 6902 modifications of selected resources. |
|
Reusable, optional bundles of resources and patches included by an overlay. |
Generators and hash suffixes
A generator creates a ConfigMap or Secret named, for example, bookshelf-config-7h2m9k4t5d. The suffix is a hash
of the content, and Kustomize rewrites every reference to the generated name. The consequence is the important part:
when the content changes, the name changes, so the Deployment’s Pod template changes, and Kubernetes performs a
rolling update. This solves the classic problem covered in
ConfigMaps — editing a ConfigMap does not restart the Pods that consume it.
Set options.disableNameSuffixHash: true only when something outside the cluster must reference a fixed name.
A secretGenerator is a convenience for producing the object, not a way to protect it: literals and files you feed
it still live in the repository in clear text. For real secret handling, see
Secrets and the sealed-secret options in
GitOps and CI/CD.
Strategic-merge versus JSON 6902 patches
patches accepts two styles:
-
A strategic-merge patch is a partial manifest that is merged into the target; list items such as containers are matched by their merge key (
name). It reads like ordinary YAML and is the right choice for most edits. -
A JSON 6902 patch is an explicit list of
op/path/valueoperations (add,replace,remove). It is the tool for edits a merge cannot express, such as removing a field or addressing a list item by index.
Both are attached with a target selector (group, version, kind, name, label selector), so one patch file can be
applied to several resources.
The Bookshelf layout
The recommended structure keeps one base and one thin overlay per environment. This is the layout used when the Bookshelf application is delivered in GitOps and CI/CD, with rollouts following Deployment Strategies:
k8s/
├── base/
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ ├── catalog-api.yaml # Deployment + Service
│ ├── reviews-api.yaml # Deployment + Service
│ ├── web.yaml # Deployment + Service
│ └── postgres.yaml # StatefulSet + headless Service
├── components/
│ └── metrics/
│ └── kustomization.yaml # kind: Component
└── overlays/
├── dev/
│ ├── kustomization.yaml
│ └── replicas-patch.yaml
└── prod/
├── kustomization.yaml
├── resources-patch.yaml
└── hpa.yaml
The base lists its files and common settings:
# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: bookshelf
resources:
- namespace.yaml
- catalog-api.yaml
- reviews-api.yaml
- web.yaml
- postgres.yaml
labels:
- pairs:
app.kubernetes.io/part-of: bookshelf
configMapGenerator:
- name: bookshelf-config
literals:
- LOG_LEVEL=info
The dev overlay stays close to the base and lowers the footprint:
# k8s/overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
images:
- name: ghcr.io/example/bookshelf-catalog-api
newTag: 1.1.0-dev
replicas:
- name: catalog-api
count: 1
patches:
- path: replicas-patch.yaml
The prod overlay adds an HPA, a production image tag, resource limits and
an optional component:
# k8s/overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- hpa.yaml
components:
- ../../components/metrics
images:
- name: ghcr.io/example/bookshelf-catalog-api
newTag: 1.0.0
replicas:
- name: catalog-api
count: 3
configMapGenerator:
- name: bookshelf-config
behavior: merge
literals:
- LOG_LEVEL=warn
patches:
- path: resources-patch.yaml
- target:
kind: Deployment
name: web
patch: |-
- op: replace
path: /spec/template/spec/containers/0/imagePullPolicy
value: IfNotPresent
The strategic-merge patch referenced above only names what it changes:
# k8s/overlays/prod/resources-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalog-api
spec:
template:
spec:
containers:
- name: catalog-api
resources:
requests:
cpu: 250m
memory: 384Mi
limits:
memory: 512Mi
Preview and apply each environment:
kubectl kustomize k8s/overlays/prod | head -40
kubectl apply -k k8s/overlays/dev
kubectl get deploy -n bookshelf
NAME READY UP-TO-DATE AVAILABLE AGE
catalog-api 1/1 1 1 12s
reviews-api 1/1 1 1 12s
web 1/1 1 1 12s
A Component (kind: Component, apiVersion: kustomize.config.k8s.io/v1alpha1) packages an optional feature — here, scrape annotations and a ServiceMonitor — so an overlay opts in with one line instead of copying patches
between overlays. Resource sizing itself is explained in
Resource Management, and the hpa.yaml in
Autoscaling.
Kustomize or Helm?
Neither replaces the other; they answer different needs, and many teams use both (Helm for third-party software, Kustomize for their own applications, or Kustomize layered over a rendered chart).
| Question | Kustomize | Helm |
|---|---|---|
Approach |
Patch plain YAML; no template language. |
Render Go templates from values into YAML. |
Reuse by third parties |
Possible through remote bases, but there is no packaging or versioning story. |
Charts are versioned, published to repositories and OCI registries, and discoverable on Artifact Hub. |
Per-environment differences |
Overlays; ideal when environments differ by a few patches. |
A values file per environment; ideal when the differences are many and structured. |
Release tracking and rollback |
None of its own; relies on Git history and the cluster’s Deployment revisions. |
Releases with numbered revisions, |
Learning curve and debugging |
Low; the rendered output is the input plus patches. |
Higher; template logic can hide what will be generated (use |
Built into kubectl |
Yes ( |
No; separate binary. |
A practical rule: if you own the manifests and need two to five environments, start with Kustomize; if you distribute the application to other teams or need parameterised installs, package it as a chart.