Kustomize at scale: the structure of kustomization.yaml, the difference between the Flux Kustomization CRD and the Kustomize binary, overlay patterns for multiple environments, post-build customization, and advanced features.

In episode 6 you deployed your first application through a Flux Kustomization. Now we go deeper: Kustomize as a templating language, and how Flux uses it at scale. This episode answers the question "how do I manage dozens of applications across many environments without duplicating YAML".
We'll dissect the kustomization.yaml structure, compare the Kustomization CRD with the Kustomize binary, learn the overlays pattern, then the post-build features and advanced features that only exist in Flux.
Kustomize is a tool that merges manifests without templating. A single kustomization.yaml file declares resources and transformations:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
namePrefix: prod-
nameSuffix: -v2
commonLabels:
app: webapp
managed-by: flux
commonAnnotations:
owner: platform-team
images:
- name: nginx
newTag: 1.27.3Commonly used elements:
resources — the list of YAML files or other directories to merge.namePrefix / nameSuffix — adds a prefix or suffix to all resource names so they don't collide across environments.commonLabels / commonAnnotations — injects labels and annotations into all resources at once.images — overrides image tags without modifying the original files.Tip
Kustomize is purely declarative and idempotent: run it any number of times, and the result is the same. That's what makes it a better foundation for GitOps than drift-prone templating.
Instead of writing ConfigMaps by hand, use generators so Flux always refreshes their values when there's a change:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: app-config
files:
- config/app.properties
literals:
- LOG_LEVEL=info
secretGenerator:
- name: app-secret
envs:
- secret.envPatches modify existing manifests without rewriting them. There are two styles: strategic merge and JSON patch:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- target:
kind: Deployment
name: webapp
patch: |-
spec:
template:
spec:
containers:
- name: webapp
resources:
limits:
memory: 512Mi[
{
"op": "add",
"path": "/spec/template/spec/containers/0/env",
"value": [
{
"name": "FEATURE_X",
"value": "true"
}
]
}
]Don't confuse them: Flux's kind: Kustomization is not the Kustomize binary. The Kustomize binary only renders YAML on the client side; the Flux CRD is a controller that runs Kustomize inside the cluster and reconciles continuously. The differences:
| Aspect | Kustomize Binary | Flux Kustomization CRD |
|---|---|---|
| Execution location | Client (laptop/CI) | In-cluster controller |
| Reconciliation | None (one-shot) | Continuous per interval |
| Health assessment | None | Built-in: Deployment, Pod, Service, etc. |
| Dependency | None | dependsOn between Kustomizations |
| Prune | None | prune: true for GC |
| Output | YAML files | Applied directly to the cluster |
Flux features that don't exist in the binary:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 10m
timeout: 5m
dependsOn:
- name: infrastructure
path: ./apps
prune: true
wait: true
sourceRef:
kind: GitRepository
name: fleethealthChecks — waits for resources to actually become healthy before considering it a success.timeout — the limit for one reconciliation cycle (default 5 minutes).dependsOn — other Kustomizations that must finish and be healthy first.The most common way to manage many environments is the base + overlays pattern:
apps/webapp/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── overlays/
├── dev/
│ └── kustomization.yaml
├── staging/
│ └── kustomization.yaml
└── prod/
└── kustomization.yamlbase holds the neutral manifests, while each overlay references the base and overrides the environment-specific parts:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
namePrefix: prod-
commonLabels:
env: prod
replicas:
- name: webapp
count: 4
images:
- name: webapp
newTag: 1.2.0Then create a Flux Kustomization per environment:
flux create kustomization webapp-prod \
--source=fleet \
--path="./apps/webapp/overlays/prod" \
--prune=true \
--interval=5mNote
The DRY principle really applies: one deployment definition in base, and each environment only stores its delta. Major changes are done once in base and automatically spread to all overlays.
Flux adds a layer beyond Kustomize: post-build variable substitution. This is covered in detail in episode 11, but the gist is written here because of its close tie to Kustomize:
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
spec:
path: ./apps/webapp/overlays/prod
postBuild:
substitute:
cluster: production
substituteFrom:
- kind: ConfigMap
name: cluster-varsThe values from substitute and substituteFrom replace dollar-signed variables throughout the YAML before it's applied.
Warning
Remember the execution order: Flux runs the Kustomize build first (merge, patch, generators), then performs variable substitution on the result. So variables can be used in any field of the rendered manifests, but they can't change the structure of kustomization.yaml itself.
A few options that often come up in production:
force: true — replaces resources with immutable fields (e.g. Deployment selector label names). Use with care because Flux deletes then recreates the resource.prune: true — garbage collection for resources that are no longer in Git. Required at the application level.serviceAccountName — the Kustomization runs with a specific service account identity, important for tenancy (episode 12).wait: true — Flux waits for all resources to become healthy before marking success, useful when other Kustomizations depend on it.flux create kustomization webapp-prod \
--source=fleet \
--path="./apps/webapp/overlays/prod" \
--prune=true \
--force=true \
--wait=true \
--interval=5mKustomize together with Flux is the best way to keep your repository lean:
kustomization.yaml manages resources, generators, prefix/suffix, labels, and images.Your manifests can now scale. In episode 8 we'll manage Helm charts: HelmRepository as a source, HelmRelease for releases, values management strategies, and the install, upgrade, and rollback lifecycle. See you!