ConfigMaps: configuration outside the image
In chapter 17 the same EVENT_BUS_URL was copied into four Deployments. A ConfigMap is a small Kubernetes object that holds settings in one place, separate from your image and from your Deployments. Pods then read it as environment variables or as files.
Why decouple config
- Build once, run anywhere: the image never contains environment-specific settings.
- One place to change: a URL used by four services lives in one ConfigMap.
- Whole config files too, such as
nginx.conforconfig.json, not just single values.
A ConfigMap
It is just a list of keys and values under data. Click the parts:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: debug
EVENT_BUS_URL: http://event-bus-srv:4005
MAX_UPLOAD_MB: "10"
config.json: |
{
"featureFlags": { "newEditor": true },
"pageSize": 20
}
v1, ConfigMap
Core API group, like Pods and Services. No spec: a ConfigMap has data instead, because it does not do anything; it just holds values.
name: app-config
Pods refer to the ConfigMap by this name. It must be in the same namespace as the Pods that use it.
data
Keys and values, all text. Like env vars, numbers and true need quotes: "10".
A whole file as one value
The key config.json holds a multi-line value. | means "keep the following lines exactly as written". Mounted as a volume, this becomes a real file called config.json.
Three ways to use it
containers:
- name: posts
image: micro-posts:1.0
env:
- name: LOG_LEVEL # name inside the container
valueFrom:
configMapKeyRef:
name: app-config # which ConfigMap
key: LOG_LEVEL # which key in it
Pick single keys. The env var name can differ from the key. Instead of value: you write valueFrom:. Everything else from chapter 16 stays the same: your app reads process.env.LOG_LEVEL.
containers:
- name: posts
image: micro-posts:1.0
envFrom:
- configMapRef:
name: app-config
Every key becomes an env var with the same name: LOG_LEVEL, EVENT_BUS_URL, MAX_UPLOAD_MB. Short and tidy. Keys that are not valid env var names, like config.json, are skipped.
spec:
containers:
- name: posts
image: micro-posts:1.0
volumeMounts:
- name: config # matches the volume below
mountPath: /etc/config # folder inside the container
readOnly: true
volumes:
- name: config
configMap:
name: app-config
$ kubectl exec deployment/posts-depl -- ls /etc/config
EVENT_BUS_URL LOG_LEVEL MAX_UPLOAD_MB config.json
$ kubectl exec deployment/posts-depl -- cat /etc/config/config.json
{ "featureFlags": { "newEditor": true }, "pageSize": 20 }
Each key becomes a file in the folder; its value is the file's content. Best for real config files your app or a tool (nginx, Prometheus) reads from disk.
Creating one with kubectl
$ kubectl create configmap app-config \
--from-literal=LOG_LEVEL=debug \
--from-literal=MAX_UPLOAD_MB=10Values typed on the command line.
$ kubectl create configmap nginx-config --from-file=nginx.confThe file name becomes the key, the content the value. Point it at a folder to add every file in it.
# app.env
LOG_LEVEL=debug
MAX_UPLOAD_MB=10
$ kubectl create configmap app-config --from-env-file=app.envReuse a .env file you already have. Each line becomes a key.
$ kubectl create configmap app-config --from-literal=LOG_LEVEL=debug \
--dry-run=client -o yaml > app-config.yaml
$ kubectl apply -f app-config.yaml
$ kubectl get configmap app-config -o yamlBest of both: generate the YAML once with --dry-run, keep it in Git, and apply it like everything else.
What happens on a change
You edit the ConfigMap and apply it. Do running Pods see the new value? Try it:
| Used as | After you change the ConfigMap |
|---|---|
env var (valueFrom, envFrom) | Never updated in a running container. Restart: kubectl rollout restart deployment/<name> |
| volume (files) | Files are updated after a short delay (up to about a minute). Your app must re-read them. Not updated if mounted with subPath. |
kubectl rollout restart on the Deployments that use it. Every Pod then starts fresh with the new values.Fixing the blog app
Back to chapter 17: the event bus URL was repeated in four Deployments. Move it into one ConfigMap:
Before (×4)
env:
- name: EVENT_BUS_URL
value: http://event-bus-srv:4005After
# services-config.yaml (once)
apiVersion: v1
kind: ConfigMap
metadata:
name: services-config
data:
EVENT_BUS_URL: http://event-bus-srv:4005
POSTS_URL: http://posts-srv:4000
COMMENTS_URL: http://comments-srv:4001
QUERY_URL: http://query-srv:4002
MODERATION_URL: http://moderation-srv:4003
# in every Deployment's container
envFrom:
- configMapRef:
name: services-configRename a Service now and you change one line, not four. Each service simply ignores the URLs it does not need.
Good to know
- Not for secrets. Anyone who can read the ConfigMap sees the values in plain text. Passwords go in a Secret, next chapter.
- Size limit: 1 MiB per ConfigMap. It is for settings, not data.
- Missing ConfigMap = Pod does not start. It waits in
CreateContainerConfigErroruntil the ConfigMap exists. Addoptional: trueto the reference if it may be absent. immutable: truelocks a ConfigMap: safer, and lighter on the cluster. To change it you create a new one, sayapp-config-v2, and point the Deployment at it, which also triggers a rolling update.
Recap
- A ConfigMap holds non-secret settings outside the image.
- Use single keys with
valueFrom.configMapKeyRef, all keys withenvFrom, or files with aconfigMapvolume. - Env vars never change in a running container; files update after a short delay.
- After changing a ConfigMap,
kubectl rollout restartthe Deployments that use it.
Quick check
LOG_LEVEL in the ConfigMap and apply. kubectl apply on the Deployment says "unchanged" and the Pods still log at debug. Why?Show answer
The Deployment's YAML did not change, only the ConfigMap did, so no rollout happened. The Pods still hold the env var they got at start. Run kubectl rollout restart deployment/posts-depl.
ConfigMaps: common questions
Do Pods see ConfigMap changes automatically?
Env vars never change in a running container; restart the Deployment with kubectl rollout restart. Files mounted from a ConfigMap volume are refreshed after a short delay.