ConfigMap & Secret generators
Generate config with hashes.
A ConfigMap is one of the simplest objects in Kubernetes (the system that runs your applications, packaged into containers, across a fleet of machines): a bag of plain, non-secret settings stored as key/value pairs. It looks harmless to hand-write. You type the YAML (the plain-text format Kubernetes reads), commit it to Git (the version-control system that records every change to your files), and apply it to the cluster. A week later you change one value, apply again, and nothing happens. The running pods (a pod is one or more containers that run together as a single unit) keep serving the old value. This is the quiet bug that eats an afternoon: the config is right in Git and right in the cluster, but the process inside the container never picked up the change. Kustomize (a tool that assembles and patches Kubernetes YAML without templates) has a generator feature that closes this gap by design, and understanding why it works is the whole lesson.
Generating Instead Of Hand-Writing
Instead of writing a ConfigMap or a Secret (its sensitive-value cousin, meant for passwords, tokens, and keys) by hand, you point Kustomize at source data you already have and let it build the finished object. Three kinds of source cover almost everything: a few inline values typed straight into the config, a real file on disk that you want mounted word for word, and an environment file of KEY=VALUE lines. You declare the source in a file named kustomization.yaml, and the generator turns it into a finished resource at build time.
pool.size=8timeout.ms=3000
DB_USER=appDB_PASSWORD=supersecret
configMapGenerator:- name: app-configliterals:- LOG_LEVEL=infofiles:- app.propertiessecretGenerator:- name: db-credsenvs:- db.env
$ kustomize build .
apiVersion: v1data:LOG_LEVEL: infoapp.properties: |pool.size=8timeout.ms=3000kind: ConfigMapmetadata:name: app-config-6ct58987ht---apiVersion: v1data:DB_PASSWORD: c3VwZXJzZWNyZXQ=DB_USER: YXBwkind: Secretmetadata:name: db-creds-4d2f9bk7h8type: Opaque
Two things jump out of that output. The literal value and the file both landed inside the ConfigMap's data. And the names are not the ones you asked for. You wrote app-config and db-creds, but the tool emitted app-config-6ct58987ht and db-creds-4d2f9bk7h8. That trailing gibberish is the whole point of the feature.
The Fingerprint In The Name
A hash is a fingerprint of some data. Feed the same bytes in and you always get the same short string out. Change a single byte and the string comes out completely different, with no resemblance to the one before it. Kustomize computes a hash of the ConfigMap's contents and staples it onto the name as a suffix. So the name is no longer a label you picked. It is a fingerprint of what sits inside. Two ConfigMaps with identical data get the same suffix. Change one character of one value and the suffix changes.
Here is why that fixes the stale-config bug. A Deployment (the Kubernetes object that keeps a set of identical pods running and rolls out updates to them) only replaces its pods when the pod template, the blueprint it stamps each new pod from, actually changes. Think of a building superintendent who sends a repair crew only when the blueprint on file changes. Swap the furniture inside apartment 4B and nobody gets dispatched, because the blueprint still reads the same. Renumber the unit to 4B-v2 and the blueprint now differs, so the crew shows up. Mounting a ConfigMap by a fixed name and editing its contents is swapping the furniture. Baking the fingerprint into the name is renumbering the unit. Kustomize rewrites every reference to point at the hashed name, so when the data changes, the name changes, the pod template changes, and Kubernetes rolls the pods on its own.
Watch A Change Ripple Through
Add a Deployment that reads the ConfigMap two ways, once as environment variables and once as a mounted file, and list it under resources in the same kustomization.yaml.
apiVersion: apps/v1kind: Deploymentmetadata:name: webspec:replicas: 3selector:matchLabels:app: webtemplate:metadata:labels:app: webspec:containers:- name: webimage: nginx:1.27envFrom:- configMapRef:name: app-configvolumeMounts:- name: cfgmountPath: /etc/appvolumes:- name: cfgconfigMap:name: app-config
$ kustomize build . | grep -n app-config
9: name: app-config-6ct58987ht37: name: app-config-6ct58987ht45: name: app-config-6ct58987ht
You wrote app-config three times in your source: once in the generator, once under envFrom (the field that loads a ConfigMap's keys in as environment variables), and once under volumes. All three came out rewritten to the same hashed name. Now change one value and rebuild.
$ sed -i 's/LOG_LEVEL=info/LOG_LEVEL=debug/' kustomization.yaml$ kustomize build . | grep -n app-config
9: name: app-config-9hf2t5k8db37: name: app-config-9hf2t5k8db45: name: app-config-9hf2t5k8db
One edited value, and every reference moved to a brand-new name in lockstep. When you apply this, the Deployment's pod template now names app-config-9hf2t5k8db where it used to name app-config-6ct58987ht. That is a real change to the template, so Kubernetes runs a rolling update (replacing the pods a few at a time so the app never fully goes down), and the new pods come up reading LOG_LEVEL=debug. No manual restart. No kubectl rollout restart. No forgetting.
Secrets Are Encoded, Not Locked
Look again at the Secret in that first build. DB_PASSWORD came out as c3VwZXJzZWNyZXQ=, which feels protected until you decode it. base64 (a reversible text encoding that repacks bytes into a small set of safe characters) is not encryption. It hides nothing from anyone willing to run one command.
$ echo 'c3VwZXJzZWNyZXQ=' | base64 -d
supersecret
A secretGenerator does exactly one transformation. It base64-encodes your values and wraps them in a Secret object. It does not encrypt anything. The encoded blob in the built YAML is trivially reversible, and worse, the source it read from (the db.env file, or literals typed into kustomization.yaml) sits in your repository in clear text the moment you commit it. For a defender, that is the first thing to grep for in a code review: real passwords, tokens, or keys living in an env file next to a kustomization.yaml.
Tuning The Generators
You can steer the generators with a generatorOptions block. The two knobs you will reach for most are stamping consistent labels and annotations onto everything generated, and turning the hash suffix off. Labels matter more than they look, because they are how you find and clean up these objects later.
generatorOptions:disableNameSuffixHash: falselabels:managed-by: kustomizeannotations:origin: generated
Setting disableNameSuffixHash to true gives you a stable, predictable name, plain app-config with no suffix, which you sometimes need when another tool or a fixed reference outside Kustomize expects a known name. The cost is steep. You have handed back the exact safety you came for. With no changing suffix, editing the data no longer changes the pod template, and your pods go right back to quietly running stale config until something else restarts them. In an overlay (a folder that layers environment-specific changes on top of a shared base configuration) you can also give a generator a behavior of merge or replace, so you patch environment-specific values on top of a base ConfigMap instead of redefining the whole thing.
The Old Copies Pile Up
Every hash change mints a new object, and Kustomize never deletes the previous one for you. Apply your changes a few times over a month and the cluster quietly fills with orphaned generations that nothing mounts anymore.
$ kubectl get configmap -l managed-by=kustomize
NAME DATA AGEapp-config-6ct58987ht 2 6dapp-config-9hf2t5k8db 2 12m
The six-day-old copy is dead weight. The live Deployment points at the twelve-minute-old one, and nothing references the older version. A handful of stale ConfigMaps is harmless clutter. The same pattern with Secrets is not: a password you rotated stays readable inside the old Secret for as long as that object survives, which is real attack surface sitting in plain sight. Neither kustomize build nor a plain kubectl apply -k removes these for you. Prune them with kubectl apply -k . --prune scoped to a label selector, or let a GitOps tool (one that continuously syncs your cluster to match a Git repository), such as Argo CD or Flux, reap them on each sync.
Prove The Change Landed
Do not trust that a config change reached the process just because kustomize build printed a new hash. Check it against the running app. First confirm the rollout finished, then read the value straight out of a live pod.
$ kubectl rollout status deploy/web$ kubectl exec deploy/web -- printenv LOG_LEVEL
deployment "web" successfully rolled outdebug
If printenv still says info after an apply, your pods did not roll, and the first thing to check is whether disableNameSuffixHash got switched on somewhere in your generatorOptions. That one flag is the difference between config that ships and config that quietly rots on old pods.
Try this
Run kustomize build . on a scratch host or disposable cluster and read the output against what this lesson described. Then change one input so it fails, and re-run: the error you get is the one you will meet in production.
Takeaway
The trap worth remembering here: a secretGenerator is not a vault. Check that on your own systems before you need to, because it is cheaper to find on a quiet afternoon than during an incident.