Sign in
← Kubernetes: Kustomize and Helm
5 min · easy · Lesson 1 of 9

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.

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

Done reading?

Mark the lesson as read to move your track progress forward.

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.

Get access for $49

Tell the agent: use MCP izi, give me lesson k8s-packaging-01-kustomize-base