$linuxjunkies
>

Use Kustomize (Without Templates)

Manage Kubernetes manifests without templates using Kustomize bases, overlays, patches, and generators — applied with plain kubectl -k.

IntermediateUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • A working Kubernetes cluster (local kind/minikube or remote) with kubectl configured
  • kubectl 1.14 or later installed and pointed at your cluster
  • Basic familiarity with Kubernetes Deployments and Services
  • kustomize CLI installed (standalone binary recommended over the kubectl-embedded version)

Kustomize lets you manage Kubernetes manifests through plain YAML layering — no templating engine, no new syntax to learn. It ships inside kubectl (since 1.14) and is also available as a standalone binary. The core idea: write a reusable base, then apply environment-specific overlays on top without touching the originals. Patches, name prefixes, config generators — all composable, all plain YAML.

How Kustomize Thinks

Every Kustomize directory needs a kustomization.yaml file. That file declares what resources to include, what patches to apply, and what generators to run. kubectl -k reads this file, assembles the final manifest in memory, and streams it to the cluster — nothing is written to disk unless you redirect output.

  • Base: canonical, environment-agnostic manifests shared across all environments.
  • Overlay: a directory that points at a base and layers differences on top of it.
  • Patch: a fragment of YAML (strategic merge) or a JSON 6902 patch that modifies a specific resource.
  • Generator: a built-in instruction to produce a ConfigMap or Secret from files or literals.

Install and Verify

kubectl embeds a version of Kustomize, but it often lags behind. For serious use, install the standalone binary to get the current release.

Standalone binary (all distros)

curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/

Debian / Ubuntu (via apt)

sudo apt install -y kustomize

Fedora / RHEL family

sudo dnf install -y kustomize

Arch

sudo pacman -S kustomize

Confirm the version:

kustomize version

Build a Base

Create a directory layout for a small web application. The base holds the Deployment and Service that every environment shares.

mkdir -p myapp/base myapp/overlays/dev myapp/overlays/prod

Write the Deployment:

cat > myapp/base/deployment.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: nginx:1.27
          ports:
            - containerPort: 80
EOF

Write the Service:

cat > myapp/base/service.yaml <<'EOF'
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 80
EOF

Register both files in the base kustomization.yaml:

cat > myapp/base/kustomization.yaml <<'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - deployment.yaml
  - service.yaml
EOF

Preview the base output — nothing is applied yet:

kustomize build myapp/base

Create Overlays

Overlays reference the base via a relative path and stack their own customizations on top. Start with the dev overlay.

Dev overlay

cat > myapp/overlays/dev/kustomization.yaml <<'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namePrefix: dev-
commonLabels:
  env: dev

resources:
  - ../../base
EOF

Preview — every resource name will be prefixed with dev-:

kustomize build myapp/overlays/dev

Prod overlay with a replica patch

Production needs more replicas. Write a strategic merge patch — only the fields you care about, merged by Kubernetes field-matching rules:

cat > myapp/overlays/prod/replica-patch.yaml <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 5
EOF
cat > myapp/overlays/prod/kustomization.yaml <<'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namePrefix: prod-
commonLabels:
  env: prod

resources:
  - ../../base

patches:
  - path: replica-patch.yaml
EOF

Patches in Detail

Kustomize supports two patch styles. Use strategic merge patches for most changes; use JSON 6902 patches when you need surgical array manipulation (adding or removing a specific list element).

JSON 6902 example: change the container image

cat > myapp/overlays/prod/image-patch.yaml <<'EOF'
- op: replace
  path: /spec/template/spec/containers/0/image
  value: nginx:1.27-alpine
EOF

Reference it in kustomization.yaml with explicit target coordinates:

cat >> myapp/overlays/prod/kustomization.yaml <<'EOF'

  - path: image-patch.yaml
    target:
      group: apps
      version: v1
      kind: Deployment
      name: myapp
EOF

A cleaner, Kustomize-native alternative for image replacement is the images field — no patch file needed:

# add this block to kustomization.yaml instead of a JSON patch
images:
  - name: nginx
    newTag: "1.27-alpine"

ConfigMap and Secret Generators

Generators create ConfigMaps or Secrets from literals or files and, crucially, append a content hash to the resource name. When the data changes, the hash changes, which forces a rolling update automatically.

ConfigMap from a file

echo "LOG_LEVEL=info" > myapp/overlays/dev/app.env
cat >> myapp/overlays/dev/kustomization.yaml <<'EOF'

configMapGenerator:
  - name: myapp-config
    envs:
      - app.env
EOF

Build and inspect — the ConfigMap name will include a hash suffix like dev-myapp-config-6ft8km9b5t. Kustomize automatically rewrites all references to that ConfigMap inside the same build so your Deployment picks up the new name.

Secret generator

cat >> myapp/overlays/prod/kustomization.yaml <<'EOF'

secretGenerator:
  - name: myapp-tls
    files:
      - tls.crt
      - tls.key
    type: kubernetes.io/tls
EOF

Never commit real secret files. Use a secrets management solution (Sealed Secrets, External Secrets Operator) and only commit the generator stub.

Apply with kubectl -k

Once you are satisfied with the built output, apply an overlay directly:

# dry-run first
kubectl apply -k myapp/overlays/prod --dry-run=server

# real apply
kubectl apply -k myapp/overlays/prod

To see exactly what will be sent to the API server before applying:

kustomize build myapp/overlays/prod | kubectl apply --dry-run=server -f -

Verify

# check resources were created with the expected prefix and labels
kubectl get all -l env=prod

# confirm replica count
kubectl get deployment prod-myapp -o jsonpath='{.spec.replicas}'

Output will show 5 for the replica count if your patch applied correctly.

Troubleshooting

  • "no such file or directory" on build: All paths inside kustomization.yaml are relative to that file's directory. Double-check relative paths in resources and patches.
  • Patch not matching: Strategic merge patches match by kind + metadata.name. If you have a namePrefix set in the overlay, the patch must use the base name (without the prefix); Kustomize applies the prefix after merging.
  • ConfigMap hash changing unexpectedly: Add generatorOptions: {disableNameSuffixHash: true} to disable the hash if you need a stable name — but you lose the automatic rollout trigger.
  • kubectl version mismatch: The embedded Kustomize in kubectl may not support newer fields. Use the standalone kustomize build | kubectl apply -f - pipeline when in doubt.
  • "accumulating resources" errors: A resource listed in resources cannot also be a patch target in the same layer. Move the resource to the base or restructure the overlay.
tested on:Ubuntu 24.04Fedora 40Arch rollingDebian 12

Frequently asked questions

Does Kustomize replace Helm?
They solve different problems. Helm packages applications for distribution with a full templating engine; Kustomize layers plain YAML for operational environment management. Many teams use both: Helm to install a chart, Kustomize overlays to customize the output for each cluster.
Can I use Kustomize with GitOps tools like Flux or Argo CD?
Yes. Both Flux and Argo CD have native Kustomize support. Point them at an overlay directory and they will run the equivalent of kustomize build and apply the result on every reconciliation.
Why does my ConfigMap get a random-looking suffix on its name?
That suffix is a content hash. When the ConfigMap's data changes, the hash changes, the name changes, and Kubernetes triggers a new rollout of any workload referencing it. Set disableNameSuffixHash: true under generatorOptions if you need a stable name.
How do I share a patch across multiple overlays without duplicating it?
Place the patch file in the base or in a shared directory, then reference it with a relative path from each overlay's kustomization.yaml. Alternatively, create an intermediate base that includes the common patch and have each overlay reference that intermediate base.
Is there a way to validate the built manifest before applying?
Yes. Pipe the build output through kubeval or kubeconformist for schema validation, or use kubectl apply --dry-run=server -f - which validates against the live API server and catches admission webhook issues too.

Related guides