ConfigMaps
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
A container image should be the same artifact in every environment; what differs between development, staging and
production — a database host, a log level, a feature flag, a whole configuration file — should be injected from
outside. A ConfigMap is the Kubernetes object for that injection: a namespaced bag of key/value pairs holding
non-confidential configuration, which Pods consume as environment variables, command-line arguments or files.
Confidential values belong in a Secret instead (Secrets). This page is written
against Kubernetes 1.37; see the section index for the full version baseline. It
builds on Pods and is used by almost every workload page that follows, for example
the log agent in DaemonSets and the initialization script in
StatefulSets.
A ConfigMap has two payload maps: data (UTF-8 strings) and binaryData (base64-encoded bytes). Key names may
contain only alphanumerics, -, _ and .. Because it is an ordinary API object, configuration is versioned,
diffable and applied with the same kubectl apply workflow as the workloads that read it.
Source: Kubernetes Docs — ConfigMaps.
Creating ConfigMaps
There are four ways to create one, and they produce the same object shape. The imperative forms are convenient for
experiments and for generating YAML (--dry-run=client -o yaml); the declarative YAML form is what belongs in version
control.
# 1. Literal key/value pairs
kubectl create configmap catalog-config \
--from-literal=LOG_LEVEL=info \
--from-literal=PAGE_SIZE=25 \
--namespace bookshelf
# 2. From a file: the key is the file name, the value the whole file content
kubectl create configmap catalog-files --from-file=application.properties --namespace bookshelf
# 2b. From a file under an explicit key, or from a whole directory (one key per file)
kubectl create configmap catalog-files --from-file=app.properties=./config/application.properties
kubectl create configmap catalog-dir --from-file=./config/
# 3. From an env file: each KEY=value line becomes one key
kubectl create configmap catalog-env --from-env-file=catalog.env
# 4. Declaratively, from a manifest (see below), then
kubectl apply -f catalog-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: catalog-config
namespace: bookshelf
data:
LOG_LEVEL: info
PAGE_SIZE: "25" # values are strings: quote numbers and booleans
application.properties: |
server.port=8080
catalog.cache.enabled=true
Source: Kubernetes Docs — ConfigMaps.
Where the book is outdated
The 2017 Kubernetes: Up and Running sampler creates its Ghost ConfigMap with a command of the form
kubectl apply cm --from-file ghost-config.js ghost-config. That is an erratum, not just an age problem: kubectl
apply consumes manifests (-f/-k) and has no --from-file flag for building an object, and cm is only a
resource short name. The command that creates a ConfigMap is kubectl create configmap ghost-config
--from-file=ghost-config.js (short form kubectl create cm). The same sampler’s Redis chapter builds a multi-file
ConfigMap, which is exactly what --from-file=<directory> or several --from-file flags produce.
Consuming a ConfigMap
A Pod can consume a ConfigMap in four ways. All of them require the ConfigMap to exist in the same namespace as the
Pod; a missing ConfigMap blocks the container from starting (CreateContainerConfigError) unless the reference is
marked optional: true.
Environment variables: env and envFrom
valueFrom.configMapKeyRef picks one key and renames it; envFrom.configMapRef imports every key at once (optionally
with a prefix). Keys that are not valid environment-variable names are skipped with an event.
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalog-api
namespace: bookshelf
spec:
replicas: 2
selector:
matchLabels:
app: catalog-api
template:
metadata:
labels:
app: catalog-api
spec:
containers:
- name: catalog-api
image: ghcr.io/example/bookshelf-catalog-api:1.0.0
env:
- name: CATALOG_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: catalog-config
key: LOG_LEVEL
envFrom:
- configMapRef:
name: catalog-config
prefix: CATALOG_
# $(VAR) references let an env var appear in command arguments
args: ["--page-size=$(CATALOG_PAGE_SIZE)"]
Environment variables are read once, when the container starts. They never change for the life of the container, so changing the ConfigMap has no effect on a running Pod (see Update propagation).
Volumes: a directory of files
Mounting the ConfigMap as a volume turns each key into a file. This is the right form for real configuration files.
containers:
- name: catalog-api
image: ghcr.io/example/bookshelf-catalog-api:1.0.0
volumeMounts:
- name: config
mountPath: /etc/catalog # whole-directory mount
readOnly: true
- name: config
mountPath: /app/application.properties # one file
subPath: application.properties
volumes:
- name: config
configMap:
name: catalog-config
items: # optional: choose keys and file names
- key: application.properties
path: application.properties
defaultMode: 0444
Three variants, and the trade-offs between them:
-
Whole directory (no
items, nosubPath): every key becomes a file undermountPath, and the mount hides whatever the image had in that directory. Files are projected through a symlink swap, so they update in place. -
items: only the listed keys are projected, with chosen file names and optional per-filemode. Use it to avoid exposing unrelated keys and to rename keys that are not valid file names in the target program. -
subPath: mounts a single file into an existing directory without hiding its siblings — the classic way to drop one config file next to files shipped in the image. The price is that asubPathmount is a bind mount of the file at start-up, so it never receives updates.
The sampler’s Parse chapter takes the simpler route for a Deployment plus Service: the Parse server reads its
MongoDB connection settings from environment variables (here populated from a ConfigMap with envFrom), which is
the pattern to prefer when an application accepts configuration through its environment.
The sampler’s Ghost chapter mounts the ConfigMap over /ghost/config and works around the hidden directory by
copying the file into place from the ConfigMap mount with a container command. That workaround is superseded:
subPath (one file beside the image’s own files) or items (only the keys you want) do the job declaratively, and
without a cp step that has to be kept in sync with the image.
Multi-file ConfigMaps for init scripts
The same sampler’s Redis chapter keeps several files in one ConfigMap — a primary and a replica configuration, an
init script and a sentinel configuration — and mounts them into two containers of a StatefulSet. The pattern is still
current: one ConfigMap holding related files, mounted once per container that needs them, with the script marked
executable through defaultMode or per-item mode.
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
namespace: bookshelf
data:
master.conf: |
maxmemory 64mb
maxmemory-policy allkeys-lru
replica.conf: |
replicaof redis-0.redis 6379
init.sh: |
#!/bin/sh
# pick a role from the Pod ordinal, then copy the matching file into the data volume
case "${HOSTNAME##*-}" in
0) cp /config/master.conf /data/redis.conf ;;
*) cp /config/replica.conf /data/redis.conf ;;
esac
The StatefulSet page shows this ConfigMap in context (StatefulSets).
Update propagation
Editing a ConfigMap does not restart anything. What happens next depends on how it was consumed:
| Consumption | Behaviour after the ConfigMap changes |
|---|---|
|
Never updated while the container runs; needs a new container. |
Volume (whole directory or |
Updated eventually: the kubelet re-syncs on its sync period plus its cache TTL, so expect up to about a minute. The application must re-read the files (or watch them) to notice. |
Volume with |
Never updated; needs a new container. |
Immutable ConfigMap |
Cannot change at all; replace it with a new name. |
For workloads that cannot hot-reload, the usual goal is "roll the Pods when the configuration changes". Three approaches:
-
Manual restart:
kubectl rollout restart deployment/catalog-api -n bookshelfcreates a new ReplicaSet generation (see ReplicaSets and Deployments). -
Checksum annotation: put a hash of the rendered ConfigMap in the Pod template, so any change alters the template and triggers a rolling update. Helm charts conventionally use
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}. -
Name-suffix hash: Kustomize’s
configMapGeneratorappends a content hash to the ConfigMap name and rewrites every reference, so changed content means a new name, which changes the Pod template.
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: catalog-config
literals:
- LOG_LEVEL=info
files:
- application.properties
resources:
- deployment.yaml
The official tutorial Updating configuration via a ConfigMap walks through the volume and environment cases side by side, including observing the delay before a mounted file changes.
Applications that do hot-reload can do it themselves: Spring Cloud Kubernetes watches the API for ConfigMap changes, see Kubernetes ConfigMaps and Secrets in the Spring Boot section.
Immutable ConfigMaps and the size limit
Setting immutable: true makes the data and binaryData fields read-only for the object’s lifetime. Two benefits:
accidental edits cannot break every Pod that reads the object, and the kubelet stops watching it, which reduces API
server load on clusters with many ConfigMaps. The only way to change it is to delete and recreate it, which in practice
means versioned names such as catalog-config-v2 with the Deployment updated to match.
apiVersion: v1
kind: ConfigMap
metadata:
name: catalog-config-v2
namespace: bookshelf
immutable: true
data:
LOG_LEVEL: debug
A ConfigMap, like a Secret, is capped at 1 MiB total, because it is stored in etcd. Large files (datasets, binaries, model weights) belong in a volume, an object store or an image volume (see Volumes and Ephemeral Storage), not in a ConfigMap.
Pitfalls
-
Secrets in ConfigMaps: ConfigMaps are not encrypted at rest by default and are readable by anyone with
geton them; credentials go in Secrets. -
Numbers and booleans in
datamust be quoted strings, or the API server rejects the manifest. -
Namespace boundary: a Pod cannot reference a ConfigMap from another namespace.
-
Order of precedence: when
envandenvFromdefine the same name,envwins; among severalenvFromsources, the last wins.