Kustomize: manifests as one group
How kustomization.yaml assembles scattered YAML into one unit, and why Kustomize isn't a templating engine.
Why
A single application in Kubernetes is rarely described by one file.
Deployment, Service, ConfigMap, sometimes Ingress and ServiceAccount.
Applying them one at a time with kubectl apply -f gets old fast: it's easy
to forget a file, easy to apply something other than what's in git.
Kustomize solves this without templates. It's a tool that reads a group
description from a kustomization.yaml file, builds one final stream of
objects out of the YAML files it lists, and can patch them along the way.
There's no need to install it separately: Kustomize has been built into
kubectl since version 1.14.
kustomization.yaml
The minimal file just contains a list of resources:
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
resources lists paths relative to kustomization.yaml itself. You can put
files there, folders with their own kustomization.yaml inside, or even a
URL to a remote repository. A file that isn't in the list won't make it
into the build, even if it sits in the same folder.
You apply a folder like this with the -k flag instead of -f:
kubectl apply -k .
kubectl finds kustomization.yaml, builds the group, and sends the objects
to the cluster. The group is removed the same way:
kubectl delete -k .
The file name is checked literally: kustomization.yaml,
kustomization.yml, or Kustomization. kubectl won't see a file named
kustomize.yaml and will say there's no configuration in the folder.
See the result before applying
kubectl kustomize builds the group the same way, but prints the final
YAML to stdout and sends nothing to the API server. No cluster is needed
for this:
kubectl kustomize .
The output is multi-document YAML: objects are separated by a --- line.
It's the fastest way to see what will actually go to the cluster.
The order of objects in the output doesn't have to match the order in
resources. Kustomize sorts them by kind by default: ConfigMap and Service
come before Deployment, so that when you apply the group, everything the
Deployment references is already in the cluster. For a group of three
files, it looks like this:
apiVersion: v1
kind: ConfigMap
...
---
apiVersion: v1
kind: Service
...
---
apiVersion: apps/v1
kind: Deployment
...
The same output is handy to feed further down a pipeline:
kubectl kustomize . | kubectl apply --dry-run=server -f -
This way kubectl validates the objects on the API server side, including schema validation and admission controllers, but doesn't persist them. A cluster is required here.
Folder structure
A typical layout looks like this:
app/
kustomization.yaml
deployment.yaml
service.yaml
configmap.yaml
The manifests themselves stay valid YAML. You can apply them directly too,
with kubectl apply -f deployment.yaml; Kustomize doesn't break anything in
them or add its own syntax. This is what sets the approach apart from
templating engines: there, a file with {{ }} inside stops being a valid
manifest and is useless without rendering.
Kustomize and templating engines
Helm substitutes values into templates and produces a manifest on output. Kustomize comes at it from the other side: it starts from an already valid manifest, and changes are described separately and applied on top. There are no variables, loops, or conditionals in the manifests; that's a deliberate limitation of the project.
There's a practical takeaway here. Kustomize works well when you have one set of manifests and a few variations of it (dev, stage, prod). It's a poor fit when you need to hand out a configurable package to outside people with unpredictable requirements. Helm fits better there.
What usually breaks
The -k flag takes a folder, not a file. kubectl apply -k kustomization.yaml
fails before it even reaches the cluster:
error: must build at directory: 'app/kustomization.yaml': file is not directory
The right form is kubectl apply -k . or kubectl apply -k ./app.
Paths in resources are counted from where kustomization.yaml sits, not
from the terminal's current directory. If you run the command from the repo
root while the configuration lives in app/, the paths inside are still
relative to app/. A path written from the root gets glued by Kustomize
onto the kustomization.yaml folder, and it won't find the file. The error
shows the path it tried to open:
error: accumulating resources: accumulation err='accumulating resources from 'app/deployment.yaml': evalsymlink failure on '.../app/app/deployment.yaml' : lstat .../app/app: no such file or directory': ...
The file sits in the folder but didn't make it into the build. That means
it was left out of resources. kubectl won't show an error: the group
builds without that object, and the problem only surfaces later, in the
cluster. That's why it's worth checking the list of kind entries in the
kubectl kustomize output before apply -k.
The version of Kustomize bundled with kubectl is pinned at kubectl's
release time and can differ from the standalone kustomize utility. Newer
fields from the project's docs might not work in your kubectl. kubectl version --client shows the bundled Kustomize version:
Client Version: v1.36.1
Kustomize Version: v5.8.1
If there's a mismatch, install the standalone kustomize binary and render
with that instead.
What you'll need for the tasks
Both labs work without a cluster or Docker: all you need is kubectl.
- Lab 1 (
labs/01-group-manifests): write akustomization.yamlfor a folder with three manifests, build the group withkubectl kustomize, and read the output. - Lab 2 (
labs/02-broken-build): someone else'skustomization.yamlwon't build. Work through kubectl's error, fix the paths inresources, and check that the build has everything the Deployment references. - Commands:
kubectl kustomize <folder>,grep '^kind:'on the output,kubectl version --client.
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-base # set up the starting state
./run.sh verify k8s-kustomize-base # check the current step
In short
- Kustomize has been built into kubectl since version 1.14; installing it separately isn't required.
kustomization.yamlwith aresourcesblock describes a group of manifests; paths are counted from this file.kubectl apply -k <folder>applies the group,kubectl delete -k <folder>removes it.kubectl kustomize <folder>prints the final YAML without writing to the cluster; the objects in it are sorted by kind.- The original manifests stay valid: Kustomize applies patches on top instead of rendering templates.
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 2 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-01-kustomize-base
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