Kustomize’s post-build substitution allows you to inject variables into your Kubernetes manifests after Kustomize has generated them, but before they’re applied to the cluster.
Let’s see it in action. Imagine you have a kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
configMapGenerator:
- name: app-config
literals:
- APP_ENV=staging
- DB_HOST=db.staging.svc.cluster.local
patchesStrategicMerge:
- patch-deployment.yaml
And a deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-registry/my-app:latest
env:
- name: APP_ENV
value: "default" # This will be replaced
- name: DB_HOST
value: "localhost" # This will be replaced
And a patch-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: my-app
env:
- name: LOG_LEVEL
value: "info" # This will be added by the patch
When you run kustomize build ., you get this output:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- env:
- name: APP_ENV
value: "default"
- name: DB_HOST
value: "localhost"
- name: LOG_LEVEL
value: "info"
image: my-registry/my-app:latest
name: my-app
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-m98h7f8d6k
data:
APP_ENV: staging
DB_HOST: db.staging.svc.cluster.local
Notice how APP_ENV and DB_HOST in the deployment still have their default values. Kustomize itself doesn’t automatically inject values from ConfigMapGenerator or SecretGenerator into existing fields like env.value. It primarily uses these generators to create ConfigMaps and Secrets, which your application then consumes.
This is where post-build substitution comes in. Kustomize has a vars field in its kustomization.yaml that lets you reference values from generated ConfigMaps or Secrets and substitute them into your manifests.
Here’s how you’d modify kustomization.yaml to achieve this:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
configMapGenerator:
- name: app-config
literals:
- APP_ENV=staging
- DB_HOST=db.staging.svc.cluster.local
vars:
- name: APP_ENV # The name of the variable you want to inject
objref:
kind: ConfigMap
name: app-config-m98h7f8d6k # The name of the generated ConfigMap. Kustomize appends a hash.
fieldref:
fieldpath: data.APP_ENV # The path to the field within the ConfigMap
- name: DB_HOST
objref:
kind: ConfigMap
name: app-config-m98h7f8d6k
fieldref:
fieldpath: data.DB_HOST
patchesStrategicMerge:
- patch-deployment.yaml
Crucially, you need to know the generated name of the ConfigMap or Secret. Kustomize appends a hash to the name you provide in configMapGenerator or secretGenerator for uniqueness. You can find this generated name by running kustomize build . once and looking at the output. In our example, it’s app-config-m98h7f8d6k.
Now, when you run kustomize build . again with the vars section, the output will be:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- env:
- name: APP_ENV
value: "staging" # Injected value
- name: DB_HOST
value: "db.staging.svc.cluster.local" # Injected value
- name: LOG_LEVEL
value: "info"
image: my-registry/my-app:latest
name: my-app
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-m98h7f8d6k
data:
APP_ENV: staging
DB_HOST: db.staging.svc.cluster.local
The value fields for APP_ENV and DB_HOST in the Deployment manifest have now been updated with the values from the app-config ConfigMap. This substitution happens after Kustomize has processed resources and patches but before the final output is generated.
This is incredibly useful for situations where you want to parameterize your manifests without resorting to templating engines like Helm or Go templating. You can define base configurations, generate ConfigMaps/Secrets for different environments (dev, staging, prod), and then use vars to inject those environment-specific values into your core resources like Deployments, Services, etc. It keeps your Kustomize structure clean and leverages Kubernetes’ native resource management.
The objref and fieldref are powerful. objref points to the Kubernetes object (ConfigMap, Secret, etc.) that holds the data, and fieldref specifies the exact path within that object to retrieve the value. This means you can pull values from data, stringData, or even other fields if needed.
A common pitfall is forgetting to update the name in objref when your ConfigMap/Secret name changes or when Kustomize’s hash generation changes. This is why it’s often run as a two-step process: build once to get the generated name, then update kustomization.yaml with that name, and build again. Tools like kustomize edit set nameprefix can help manage naming, but for vars, manual tracking or scripting might be necessary if you don’t want to hardcode the hashed name.
The next thing you’ll likely want to explore is using vars to substitute values into fields other than env.value, such as image tags or resource limits.