🧩 Kustomize Quick Reference

Kustomize cheat sheet / survival guide

Video: Kustomize Quick Reference (DevOps video)
Prefer to watch? This short video runs this page's main commands in a real lab, with on-screen captions and no voiceover. Click to play, or watch it on YouTube.
kustomize build <directory>
kubectl apply -k <directory>

Kustomize customizes plain Kubernetes YAML without templates. You keep a clean "base" set of
manifests, then layer "overlays" on top (dev, staging, prod) that patch, rename, relabel or
re-image things. Everything is ordinary YAML, driven by a file called kustomization.yaml.
It is built into kubectl (kubectl apply -k), and also available as a standalone kustomize
command that is often a newer version.

Install

kubectl version --client                       # kustomize is built in (kubectl apply -k, kubectl kustomize)
brew install kustomize                         # macOS and Linux (Homebrew)
sudo snap install kustomize                    # Linux (snap)
go install sigs.k8s.io/kustomize/kustomize/v5@latest    # from source
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash   # official script (read it first)
docker run --rm -v "$PWD":/work -w /work registry.k8s.io/kustomize/kustomize:v5.4.3 build .   # no install (check the tag)
kustomize version
The version built into kubectl can lag behind the standalone one. Run both --version commands if a feature is missing.

The main commands

kustomize build ./overlays/prod                # render the final YAML to the screen
kustomize build ./overlays/prod | kubectl apply -f -      # render and apply
kubectl kustomize ./overlays/prod              # same render, using kubectl's built-in copy
kubectl apply -k ./overlays/prod               # build and apply in one step
kubectl diff -k ./overlays/prod                # preview changes against the live cluster
kubectl delete -k ./overlays/prod              # delete everything the kustomization creates
kubectl get -k ./overlays/prod                 # list those resources from the cluster
kustomize build ./overlays/prod > rendered.yaml    # save the output (for review or GitOps)
kustomize create --autodetect                  # generate a kustomization.yaml from the YAML files in a folder
kustomize edit ...                             # change kustomization.yaml from the command line (see below)
kustomize cfg ...                              # inspect and manipulate YAML (advanced)

Typical layout

myapp/
  base/
    kustomization.yaml
    deployment.yaml
    service.yaml
  overlays/
    dev/
      kustomization.yaml
      replica-patch.yaml
    prod/
      kustomization.yaml
      resources-patch.yaml
      ingress.yaml
The base is complete and works on its own. Overlays only describe what is different.

A base (base/kustomization.yaml)

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

An overlay (overlays/prod/kustomization.yaml)

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production               # put everything in this namespace
namePrefix: prod-                   # rename: prod-web, prod-db ...
commonLabels:
  env: production                   # add labels (and selectors) to everything
commonAnnotations:
  owner: platform-team
resources:
  - ../../base                      # pull in the base
  - ingress.yaml                    # extra resources only prod needs
images:
  - name: nginx                     # change an image without editing the YAML
    newName: registry.example.com/nginx
    newTag: "1.27.2"
replicas:
  - name: web
    count: 5
patches:
  - path: resources-patch.yaml      # a patch file (see below)

Fields in kustomization.yaml

resources          Files, folders or URLs to include (bases go here too)
namespace          Set the namespace on every resource
namePrefix / nameSuffix   Add text to every resource name
labels             Add labels: - pairs: {env: prod}  includeSelectors: true
commonLabels       Add labels everywhere (also touches selectors: risky on existing apps)
commonAnnotations  Add annotations to everything
images             Rename or retag images
replicas           Set replica counts by workload name
patches            Change parts of resources (strategic merge or JSON patch)
configMapGenerator Create ConfigMaps from files or literals
secretGenerator    Create Secrets from files or literals
components         Reusable optional pieces you can add to an overlay
replacements       Copy a value from one field to another
helmCharts         Inflate a Helm chart as part of the build (needs --enable-helm)
generatorOptions   Options for generators (disableNameSuffixHash, labels ...)
sortOptions        Control the order of the output
transformers / generators / validators    Custom plugins (advanced)

Patches

Strategic merge patch (a partial YAML file; matched by kind and name):
# resources-patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  template:
    spec:
      containers:
        - name: web              # containers are matched by name
          resources:
            limits:
              cpu: 500m
              memory: 512Mi
patches:
  - path: resources-patch.yaml

Inline patch with a target (JSON 6902 style):
patches:
  - target:
      kind: Deployment
      name: web
    patch: |-
      - op: replace
        path: /spec/replicas
        value: 3
      - op: add
        path: /spec/template/spec/containers/0/env/-
        value: { name: LOG_LEVEL, value: debug }
Operations: add, remove, replace, move, copy, test. Paths use / and array positions (0, -).

Target selectors

target:
  kind: Deployment                  # a resource type
  name: web                         # by name (regular expressions allowed)
  namespace: default
  labelSelector: "app=web"
  annotationSelector: "team=blue"
  group: apps
  version: v1

Generators (ConfigMaps and Secrets)

configMapGenerator:
  - name: app-config
    literals:
      - LOG_LEVEL=info
      - MODE=prod
    files:
      - app.properties
    envs:
      - app.env
secretGenerator:
  - name: db-creds
    literals:
      - password=change-me
    type: Opaque
Generated names get a hash suffix (app-config-7h2k9f). Kustomize rewrites references to them,
so changing the contents changes the name and Pods roll automatically. To turn that off:
generatorOptions:
  disableNameSuffixHash: true
Do not commit real secrets. Use sealed secrets, SOPS or an external secrets operator instead.

Images, replicas and labels quickly

images:
  - name: myapp
    newTag: v2.1.0                   # only change the tag
  - name: myapp
    newName: registry.example.com/team/myapp
    digest: sha256:abc123...         # pin to a digest
replicas:
  - name: web
    count: 3
labels:
  - pairs:
      app.kubernetes.io/part-of: shop
    includeSelectors: false          # safer: does not touch selectors
    includeTemplates: true

Components (reusable add-ons)

# components/monitoring/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
patches:
  - path: add-metrics-annotations.yaml
# in an overlay:
components:
  - ../../components/monitoring
Use components for optional features you want in some overlays but not others.

Replacements (copy a value from one place to another)

replacements:
  - source:
      kind: ConfigMap
      name: app-config
      fieldPath: data.HOSTNAME
    targets:
      - select:
          kind: Ingress
        fieldPaths:
          - spec.rules.0.host
This replaces the old vars feature. Use it sparingly: too many replacements make overlays hard to follow.

Edit kustomization.yaml from the command line

kustomize edit add resource deployment.yaml
kustomize edit add resource ../../base
kustomize edit set image nginx=nginx:1.27.2
kustomize edit set namespace production
kustomize edit set nameprefix prod-
kustomize edit set replicas web=5
kustomize edit add label env:prod
kustomize edit add patch --path patch.yaml
kustomize edit add configmap app-config --from-literal=KEY=value
kustomize edit remove resource old.yaml
Very handy in CI to set the image tag: (cd overlays/prod && kustomize edit set image myapp=myapp:$GIT_SHA)

Remote bases and Helm

resources:
  - github.com/example/repo//deploy/base?ref=v1.2.3   # a folder in a git repo, pinned to a tag
  - https://example.com/manifests/app.yaml
Pin remote bases with ?ref=tag or a commit hash. Never track a moving branch in production.
helmCharts:
  - name: nginx
    repo: https://charts.bitnami.com/bitnami
    version: 15.4.0
    releaseName: web
    namespace: web
    valuesInline:
      replicaCount: 2
kustomize build --enable-helm ./overlays/prod
Helm inflation needs the helm binary installed and the flag --enable-helm (kubectl kustomize needs it too).

Build options

kustomize build ./overlays/prod --enable-helm             # allow helmCharts
kustomize build ./overlays/prod --load-restrictor LoadRestrictionsNone   # allow files outside the folder (use with care)
kustomize build ./overlays/prod -o out/                    # write each resource to its own file
kustomize build ./overlays/prod --reorder legacy           # older ordering behavior
kustomize build ./overlays/prod --enable-alpha-plugins     # allow exec/KRM function plugins (trust only known ones)
By default, a kustomization may only reference files inside its own directory (or bases it lists).

Practical one-liners

# See what will be deployed, without touching the cluster
kustomize build overlays/prod

# Deploy an overlay
kubectl apply -k overlays/prod

# Preview the difference against the cluster first
kubectl diff -k overlays/prod

# Compare two environments
diff <(kustomize build overlays/dev) <(kustomize build overlays/prod)

# Set the image tag in CI, then deploy
cd overlays/prod && kustomize edit set image myapp=registry.example.com/myapp:$GIT_SHA && cd - && kubectl apply -k overlays/prod

# Check the output is valid before applying
kustomize build overlays/prod | kubectl apply --dry-run=client -f -
kustomize build overlays/prod | kubectl apply --dry-run=server -f -

# List the kinds and names that will be created
kustomize build overlays/prod | grep -E '^(kind|  name):'

# Delete everything an overlay created
kubectl delete -k overlays/prod

# Start a kustomization from existing YAML files
cd myapp/base && kustomize create --autodetect

# Validate the output with a schema tool
kustomize build overlays/prod | kubeconform -strict -summary

# Generate a starter for a new overlay
mkdir -p overlays/staging && printf 'resources:\n  - ../../base\n' > overlays/staging/kustomization.yaml

Kustomize vs Helm

Kustomize    No templates: patch real YAML. Simple, transparent, great for your own apps and per-environment changes
Helm         Templates and packages: great for third-party software and apps with lots of settings
Together     Render a chart with helm template (or helmCharts), then patch the result with Kustomize
Argo CD / Flux   GitOps controllers: both can deploy Kustomize overlays straight from git
kubectl apply -f   Plain files with no layering
Rule of thumb: Kustomize for "my app in three environments", Helm for "install this off-the-shelf app".

Common problems

"no such file or directory" on resources    Path is wrong. Paths in kustomization.yaml are relative to that file
"security; file is not in or below"         Referencing outside the folder: restructure, or use --load-restrictor (carefully)
"kustomization.yaml" not found              The folder you pointed at has no kustomization file
"no matches for OriginalId ..."             A patch target does not match by kind, name or namespace (prefixes apply after patching)
Patch has no effect                         Wrong name, wrong kind or wrong apiVersion in the patch, or a typo in the path
"may not add resource with an already existing id"   The same resource is included twice (a base pulled in two ways)
Selector or label changes break a Deployment   commonLabels rewrote selectors, which are immutable. Use labels with includeSelectors: false
Pods do not restart when a ConfigMap changes   Static ConfigMap: use configMapGenerator so the name hash changes
kubectl apply -k uses old behavior          kubectl's built-in kustomize is older. Use the standalone kustomize build | kubectl apply -f -
"helm: executable file not found"           Install helm and add --enable-helm
YAML errors                                 Use spaces, not tabs, and check indentation. Try kustomize build to see the exact error

Tips

    Always run kustomize build first and read the output. It is just YAML, so you can review exactly what will be applied.

    Keep the base valid and deployable on its own. Overlays should only hold differences.

    Use kubectl diff -k before kubectl apply -k, especially on production.

    Prefer patches, images and replicas over copying whole files into overlays.

    Use labels with includeSelectors: false (not commonLabels) on apps that already exist, so selectors do not change.

    Use configMapGenerator so a config change automatically rolls the Pods that use it.

    Pin remote bases and Helm chart versions (?ref=v1.2.3). Do not point production at a moving branch.

    Keep secrets out of git. Use SOPS, sealed secrets or an external secrets manager.

    Set image tags from CI with kustomize edit set image, so deploys are traceable to a commit.

    Do not let overlays grow tangled. If you need many conditionals and loops, that is a sign Helm might fit better.