31.2 kustomization.yaml: resources, patches, images, namePrefix
Alright, let’s get our hands dirty with the kustomization.yaml file. This is your single source of truth for a Kustomize overlay, the control panel from which you direct the entire symphony of YAML mangling. Forget templates; we’re layering and patching like a digital archaeologist, carefully brushing away the generic to reveal the environment-specific.
The file is essentially a manifest of manifests. It tells Kustomize: “Here are the raw materials, here’s how I want you to change them, now go build me something I can actually kubectl apply.”
The resources field: Your base camp
This is non-negotiable. The resources array is your list of what Kustomize should even look at. These can be paths to other Kustomize directories (which is how you build those famous overlays) or direct paths to raw YAML files. Think of it as your ingredients list. You can’t make a cake if you don’t first declare you need flour, eggs, and sugar.
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
# Point to a base directory that has its OWN kustomization.yaml
- ../../base
# Or just slam in a specific file directly. Kustomize isn't picky.
- ./my-extra-service.yaml
# You can even use a URL, because why not live dangerously?
# - https://github.com/kubernetes/website/blob/main/content/en/examples/pod.yaml
Why it works this way: Kustomize is declarative. You don’t tell it how to find files; you declare which files constitute your starting point. This makes your overlay perfectly reproducible. A common pitfall? Forgetting that paths are relative to the kustomization.yaml file itself, not where you’re running the kustomize build command from. Get the path wrong, and Kustomize will happily build you a beautiful, empty manifest.
The patches field: Performing surgery
This is where Kustomize gets its superpowers. While resources is your “what,” patches are your “how to change it.” You have two main ways to do this, and the distinction is crucial.
Strategic Merge Patches (the sane default): These are the patches you’re most likely to use. You give Kustomize a snippet of YAML that looks exactly like the original resource, but with only the fields you want to change or add. Kustomize uses the apiVersion, kind, name, and namespace to find the right target and merges your changes in. It’s intuitive because you’re basically writing the diff.
# kustomization.yaml
patches:
- target:
kind: Deployment
name: my-app
patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3 # Change the replica count
template:
spec:
containers:
- name: server
resources: # Add a resources block that wasn't there before
requests:
memory: "64Mi"
cpu: "250m"
JSON Patch (the scalpel): For when a strategic merge isn’t precise enough. JSON Patch uses a JSONPath-like syntax to explicitly define an operation (add, remove, replace, etc.) on a specific path. It’s more powerful but also more brittle and harder to read. You break this out when you need to modify a single element in a large array or change a key deep within a complex structure.
# kustomization.yaml
patches:
- path: ./json-patch.yaml
target:
kind: Deployment
name: my-app
# ./json-patch.yaml
- op: replace
path: /spec/template/spec/containers/0/image
value: my-app:2.1.0-hotfix # Change the exact image tag for the first container
The designers’ questionable choice: The syntax for inline vs. external patches is inconsistent. For strategic merge, you use patch: | for inline and path: for a file. For JSON Patch, you must use an external file (path:). It’s a minor annoyance that trips everyone up. Just accept it.
The images field: The easy way
You could use a patch to change an image tag. It would work. But it would be like using a rocket launcher to open a jar of pickles. The images field is a specialized, declarative shortcut for the one thing we change more than anything else: container images.
# kustomization.yaml
images:
- name: my-app # The image name used in your Pod templates
newName: my-registry.com/my-team/my-app # Optional: change the whole repo
newTag: v2.1.0 # Change just the tag
- name: nginx # Find *any* container using an image named 'nginx'
newTag: 1.25.3-alpine # And update its tag
Why this is brilliant: It’s intention-revealing. Anyone looking at your kustomization.yaml immediately knows, “Ah, we’re pinning to version v2.1.0 here.” It’s also far less error-prone than writing a patch, especially if the same image is used in multiple places. The best practice is to always use this over a patch for simple image updates.
The namePrefix field: Avoiding YAML-collision headaches
This one is deceptively simple but absolutely critical for multi-tenant or multi-environment setups. namePrefix prepends a string to the names of almost all generated resources. I say “almost all” because some things, like ServiceAccount names referenced by a Pod spec, are also updated by Kustomize’s magic to keep things consistent. It’s smart.
# kustomization.yaml
namePrefix: prod-
If your base deployment creates a Deployment named my-app, applying this overlay will generate a Deployment named prod-my-app. This is how you stop your dev, staging, and prod deployments from all having the same name and trying to fight each other for control of the cluster. It’s a namespace inside a namespace. The pitfall? It doesn’t change the name field inside container specs, like environment variables referencing a ConfigMap. For that, you’ll need a patches field or the more powerful nameSuffix and namespace fields. But for most purposes, namePrefix is your first line of defense against naming chaos.