Kustomize: transformers
namePrefix, labels, namespace, and image swaps: edits that cover the whole group of resources at once.
Why
Collecting manifests into a group doesn't get you far. Usually you need to apply the same edit to all of them: add a prefix to every name, attach a shared label, send everything to a different namespace, swap an image tag everywhere at once.
A transformer in Kustomize is a rule in kustomization.yaml that applies to
every resource in the group at build time. The source manifests don't
change by a single byte. The edit lives only in the Kustomize config and
shows up in the rendered output of kubectl kustomize.
Types
| Field | What it covers | The catch |
|---|---|---|
namePrefix, nameSuffix | the names of all resources | fixes references only in fields it knows about |
namespace | the namespace of all resources | overrides what's written in the files |
commonLabels | metadata, selectors, the Pod template | an immutable-field error on a live Deployment |
labels | metadata, selectors, and the template, your choice | by default doesn't touch selectors |
commonAnnotations | annotations on all resources and the Pod template | annotations have no selectors |
images | the image's name, tag, or digest | the image string is matched literally |
The order of fields in kustomization.yaml doesn't affect the result:
Kustomize itself decides the order transformers apply in. The reliable way
to check the result is a render, not reasoning: kubectl kustomize . shows
the whole thing.
namePrefix and nameSuffix
namePrefix adds a string to the start of every resource's name,
nameSuffix to the end.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
namePrefix: prod-
Deployment web becomes prod-web, Service web becomes prod-web. You
need this when one app runs as several instances in the same namespace and
the names must not collide.
Besides object names, Kustomize also rewrites the references between them
in fields it knows about. If an Ingress referenced Service web, after the
prefix the Ingress will have prod-web. Same with a ConfigMap in envFrom
and in volumes:
envFrom:
- configMapRef:
name: prod-web-settings
namespace
The namespace field sets one namespace for all resources in the group:
namespace: demo
After that, kubectl apply -k . puts the objects into demo, even if the
files themselves don't specify a namespace or specify a different one.
That's handy for keeping manifests namespace-agnostic and setting the
destination through config. The namespace itself still has to exist in the
cluster or be included in the group as a separate resource.
Common labels and annotations
commonLabels attaches key-value pairs to every object in the group:
commonLabels:
team: platform
app.kubernetes.io/part-of: shop
This is where Kustomize's main trap hides. commonLabels writes the label
into metadata.labels and, along with it, into the selectors:
spec.selector on the Deployment, spec.selector on the Service,
spec.template.metadata.labels on the Pod template. On a fresh resource
everything goes fine. On a Deployment that already exists, the selector
can't be changed, the field is immutable, and the API server rejects it
with a field is immutable error.
Only the Deployment gets rejected. The Service's selector can change, and
the same kubectl apply -k accepts the new pair for it. The running Pods
don't have that label, the Service stops finding them, and traffic
disappears even though the Pods are alive.
That's why current versions of Kustomize have deprecated commonLabels.
kubectl warns about it on every build:
# Warning: 'commonLabels' is deprecated. Please use 'labels' instead. Run 'kustomize edit fix' to update your Kustomization automatically.
The replacement is the labels block, where the behavior is set
explicitly:
labels:
- pairs:
team: platform
includeSelectors: false
With includeSelectors: false the label only lands in objects'
metadata.labels and leaves the selectors alone. That's the default value,
but it's written explicitly so the reader doesn't have to guess. If you
also need the label on the Pod, add includeTemplates: true: then it lands
in the Pod template too, while the selectors stay as they were.
includeSelectors: true behaves like commonLabels.
commonLabels still works and shows up in most other people's
repositories, so it's worth knowing both forms.
commonAnnotations attaches annotations. It writes them into every
object's metadata and into the Pod template. Annotations have no
selectors, so immutable-field problems don't come up there.
Swapping the image
The images block changes a container's image without touching the
Deployment:
images:
- name: nginx
newTag: 1.27.4-alpine
Kustomize finds containers whose image is named nginx and sets their tag
to 1.27.4-alpine. Quote a tag that looks like a number: newTag: "1.25". Without quotes, YAML reads it as a number, and the build fails with
cannot unmarshal number into Go struct field Image.images.newTag of type string.
You can also replace the image itself:
images:
- name: nginx
newName: registry.example.com/mirror/nginx
newTag: 1.27.4-alpine
- name: busybox
digest: sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
newName sets only the name, with no tag: don't append a tag there, or
the build ends up with a string that has two tags. digest is used instead
of a tag when you need to pin a specific image build. If you set both
newTag and digest, Kustomize doesn't complain and writes both:
nginx:1.27.4-alpine@sha256:.... Kubernetes pulls the image by digest from
a string like that, the tag stays as a note for humans.
This is the most common transformer in real work. When rolling out a new
version, CI edits a single line in kustomization.yaml instead of hunting
for the tag across every manifest.
The match is against the image string exactly as it's written in the
manifest. For docker.io/library/nginx:1.24, a rule with name: nginx
silently does nothing, you need the full name: docker.io/library/nginx.
The build passes with no errors or warnings.
What usually breaks
namePrefix fixes references only in fields Kustomize knows about. A
Service name hardcoded as a string in an environment variable or in a
ConfigMap's text keeps the old name:
env:
- name: BACKEND_URL
value: http://api
Spots like that are fixed with a patch or the replacements transformer
(patches are the next lesson).
commonLabels on a live Deployment gives an immutable-selector error, and
the Service in the same rollout silently loses its Pods. The fix is
switching to labels with includeSelectors: false and running
kubectl apply -k again. Recreating the Deployment also clears the error,
but with downtime. Catching the problem before the rollout is easier with
kubectl diff -k .: it runs the changes through the API server in dry-run
mode and shows the rejection without changing anything.
An images entry matched nothing, and the cluster still runs the old tag.
Kustomize doesn't warn about this. Before a rollout, it's worth checking the
resulting images: kubectl kustomize . | grep 'image:'.
What you'll need for the tasks
- Lab 1 (
labs/01-stage-copy, no cluster): assemble a copy of the app for stage with thenamespace,namePrefix,labels, andimagestransformers, without touching the selectors. - Lab 2 (
labs/02-tag-not-replaced, no cluster): animagesentry doesn't change the tag on one of the Deployments. Find the cause from the build's output and fixkustomization.yaml. - Lab 3 (
labs/03-immutable-selector, kind cluster): a rollout withcommonLabelsfailed on the Deployment and broke the Service. Get the label onto the cluster without recreating the Deployment. - Commands:
kubectl kustomize <folder>,grepover the output,kubectl apply -k,kubectl diff -k,kubectl get ... --show-labels,kubectl get endpointslice -l kubernetes.io/service-name=<name>.
Homework
Practice the theory on your own cluster: 1 step with a check. Set up the cluster with the Kubernetes locally track, the tasks live in the k8s-practice repository.
./run.sh start k8s-kustomize-transformers # set up the starting state
./run.sh verify k8s-kustomize-transformers # check the current step
In short
- A transformer changes the whole group of resources through an edit in
kustomization.yaml, the source files stay untouched. namePrefixandnameSuffixfix names and the references between objects in fields Kustomize knows about, they don't touch strings in env or text.namespacesets one namespace for every object in the group.commonLabelsreaches into selectors and fails on a live Deployment; thelabelsblock withincludeSelectors: falseavoids that.imagesswaps an image's tag, name, or digest, matching the image string literally and silently skipping anything that doesn't match.
Done reading?
Mark the lesson as read to move your track progress forward.
Lesson marked as done.
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 3 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson k8s-packaging-02-kustomize-transformers
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress