Kustomize: bases, overlays and patches

Adjust plain YAML for each environment without copying it: a base holds the manifests and an overlay lists the changes.

35 min 2 hands-on tasksArchitecture · 25% of the exam

Key ideas in this lesson · tap one for a simple explanation

What you will be able to do

  • Write an overlay that changes the namespace, name prefix, labels, image tag and replica count, without touching the base.
  • Change any field with a patch, and choose between a strategic merge patch and a JSON 6902 patch.
  • Generate a ConfigMap, explain the hash in its name, and build, compare, apply and delete with -k.

lets you keep one set of files and adjust it for each environment. Example: your Django app runs 1 replica in dev and 3 in prod, each in its own . Instead of two copies of every file, you keep one base. Then you write a small overlay for each environment that lists only the differences.

Kustomize is built into , so it is already in the exam terminal. There are no templates: the base is plain YAML that works on its own. Exam tasks usually give you a base and ask for an overlay, or give you an overlay that does not build.

How an overlay becomes objects

Base

plain YAML, shared by every environment

base/​

Overlay

the changes for one environment

overlays/​prod/​

kubectl apply -k

builds the result and applies it

Live objects

prod-api in shop-prod, 3 replicas

The overlay points at the base and lists its changes. kubectl builds the final YAML and applies it. The base files never change.

A base and an overlay

The base and each overlay are folders, each with a file called kustomization.yaml. The base's file just lists its . An overlay's file points at the base and says what to change. Here is a production overlay:

app/overlays/prod/kustomization.yaml
# Layout:
#   app/base/kustomization.yaml            resources: [deployment.yaml, service.yaml]
#   app/overlays/dev/kustomization.yaml    resources: [../../base]
#   app/overlays/prod/kustomization.yaml   this file
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
  - namespace.yaml            # apiVersion: v1, kind: Namespace, metadata.name: shop-prod
namespace: shop-prod
namePrefix: prod-
labels:
  - pairs:
      env: prod
    includeTemplates: true     # label the Pods too, leave selectors alone
images:
  - name: nginx                # every container whose image is nginx:<anything>
    newTag: "1.27"
replicas:
  - name: api                  # the base name, before the prefix
    count: 3

Always look at the result before you apply it. -k takes a kustomization folder, where -f takes a file:

Render, compare, apply, remove
kubectl kustomize app/overlays/prod                      # print the result, change nothing
kubectl kustomize app/overlays/prod | grep -nE 'name:|namespace:|image:|replicas:'
kubectl diff -k app/overlays/prod                         # what would change on the live cluster
kubectl apply -k app/overlays/prod
kubectl get -k app/overlays/prod                          # the live objects this overlay produces
kubectl delete -k app/overlays/prod
kubectl version --client                                  # also prints the bundled Kustomize version

The fields you will use most

FieldWhat it doesWatch out for
resourcesLists the files and folders to includeThe output puts Namespaces first, so one apply -k can create a namespace and fill it
namespaceSets the namespace on every objectIt does not create the Namespace. List a Namespace file under resources for that.
namePrefix, nameSuffixAdds text to every name, and fixes references to those namesNamespace names and stay as they are
labelsAdds labels. includeTemplates: true labels the too.includeSelectors: true also changes selectors, which an existing cannot accept
imagesChanges an image's name or tagMatch the image name without its tag, and quote tags such as "1.27"
replicasSets the replica count by object nameUse the base name, not the prefixed one
patchesChanges any other field (see below)Replaces the old patchesStrategicMerge and patchesJson6902
configMapGeneratorBuilds a with a hash in its nameOnly references Kustomize knows about get the new name

Patches: change any field

When no simple field does the job, write a patch. Kustomize knows two kinds, and both go in the same patches list.

KindWhat you writeGood for
Strategic merge patchA small piece of the object, with only the fields to changeAdding or changing fields, such as a memory limit or envFrom on one
JSON 6902 patchA list of steps. Each has an op (add, replace or remove), a path such as /spec/type, and a value.Removing a field, or changing one list item by its position

In a strategic merge patch, lists such as containers are merged by each item's name. So you can change one container without repeating the others. A JSON 6902 patch names no object, so it needs a target to say which one.

In kustomization.yaml: one patch of each kind
patches:
  # Strategic merge: name the object, write only what changes
  - patch: |-
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: api
      spec:
        template:
          spec:
            containers:
              - name: api                 # merged by container name
                resources:
                  limits:
                    memory: 256Mi
  # JSON 6902: operations need a target
  - target:
      kind: Service
      name: api
    patch: |-
      - op: replace
        path: /spec/type
        value: NodePort
      - op: add
        path: /metadata/labels/exposed
        value: "true"

Generated ConfigMaps

Kustomize can also write ConfigMaps for you. You list the keys; it builds the ConfigMap and adds a hash of the content to its name, such as api-config-<hash>. secretGenerator builds the same way.

In kustomization.yaml: a ConfigMap generator
configMapGenerator:
  - name: api-config
    literals:
      - MODE=production
      - CACHE_TTL=300

Kustomize rewrites every reference it knows to the hashed name: envFrom, valueFrom and volumes. (envFrom loads every key of a ConfigMap as environment variables; ConfigMaps and Secrets teaches it.) The hash is what makes a settings change roll out:

Why the name has a hash

You change a value

in kustomization.yaml

MODE=debug

New hash, new name

a new ConfigMap

api-config-<new hash>

Pod template changes

it now points at the new name

New Pods

the Deployment rolls out

The old ConfigMap stays behind until you delete it.

In the official docs· open on exam day too

Hands-on task 1 of 2ar-kust-1
~7 min6% weightArchitecture

Build a production overlay

A Kustomize base for an app called web is in /tmp/cka/kustomize/base. Without modifying the base, create an overlay in /tmp/cka/kustomize/overlays/prod that puts every resource in namespace arx-kust-prod (the overlay must create this namespace), prefixes all names with prod-, runs 3 replicas, changes the nginx image tag to 1.27, and adds the label env=prod to every resource. Apply the overlay with kubectl apply -k.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-kust-1/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-kust-1/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-kust-1/cleanup | bash
Hands-on task 2 of 2ar-kust-2
~8 min6% weightArchitecture

Generated config and patches in a staging overlay

Using the base in /tmp/cka/kustomize/base unchanged, create an overlay in /tmp/cka/kustomize/overlays/staging that deploys into the existing namespace arx-kust-staging and: generates a ConfigMap named web-config with the keys MODE=staging and LOG_LEVEL=debug; loads every key of that ConfigMap as environment variables into the web container; and changes the Service web to type NodePort. Keep the generated name hash. Apply the overlay with kubectl apply -k.

Cluster:kubectl config use-context kind-cka

  1. 1

    Set up the scenario

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-kust-2/setup | bash
  2. 2

    Solve it in your terminal

    Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under /tmp/cka/.

  3. 3

    Check your work

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-kust-2/verify | bash
    Got FAIL? Read which check failed, fix it, run it again.
  4. 4

    Clean up (optional)

    curl -fsSL https://codewithmuh.com/labs/cka/t/ar-kust-2/cleanup | bash

Finished this lesson?

0 of 2 tasks passed. Your readiness counts tasks, so run them before you move on.