ConfigMaps
ConfigMaps
Every application has configuration: database hostnames, feature flags, thread-pool sizes, log levels, trusted CORS origins. How that configuration is delivered to the application is one of the most consequential infrastructure decisions you will make. Hardcoding it into the container image — the naive approach — couples your release cycle to your configuration cycle. Changing a log level requires rebuilding and redeploying. Worse, you end up with "environment-specific images," which destroys the fundamental portability guarantee of containers.
Kubernetes solves this with ConfigMaps: a first-class API object that holds arbitrary non-sensitive configuration data as key-value pairs or entire file contents. The image stays environment-agnostic; the ConfigMap holds the environment-specific values. The same image runs in dev, staging, and production — only the ConfigMap differs. This is the 12-Factor App principle III ("Store config in the environment") expressed natively in Kubernetes.
Creating ConfigMaps
ConfigMaps can be created imperatively (useful for scripting and quick debugging) or declared as YAML manifests (the production-grade approach, stored in version control). You should always store the manifest in Git — the imperative form is ephemeral and untraceable.
The declarative YAML form is what you commit to Git and deploy through your CI pipeline. A ConfigMap manifest looks like this:
get configmap RBAC permission in that namespace. Never put credentials in a ConfigMap.Consuming ConfigMaps: Environment Variables
The most common consumption pattern injects individual keys as environment variables. The application reads them through its standard os.Getenv / process.env / System.getenv mechanism — zero code changes required if the app already reads from environment variables.
envFrom injects every key in the ConfigMap as an environment variable. If someone adds a key that collides with a system variable your application depends on (like PATH, HOME, or a library-internal variable), the behaviour is undefined and often silent. At big-tech scale, prefer explicit env[].valueFrom.configMapKeyRef entries so the contract between ConfigMap and application is explicit and auditable in code review.Consuming ConfigMaps: Volume Mounts
The volume mount pattern is essential when your application reads from a configuration file rather than environment variables — common with NGINX, Prometheus, Kafka, Redis, and legacy applications you cannot modify. Each key in the ConfigMap becomes a separate file inside the mounted directory. The file name is the key; the file content is the value.
subPath for surgical mounts: Without subPath, mounting a ConfigMap volume onto a directory replaces the entire directory. If /etc/nginx/conf.d/ already has default configs you need to preserve, use subPath: nginx.conf to mount only that one file without clobbering the rest of the directory. The trade-off: subPath mounts do not receive live updates when the ConfigMap changes — see the update behaviour section below.Update Behaviour: The Critical Difference
This is where engineers get burned in production. The update behaviour of ConfigMaps differs depending on how you consume them, and understanding this prevents outages:
- Environment variables (
env/envFrom): Injected at container start time. They are never updated in a running container. To apply a ConfigMap change, you must restart the Pod — either by deleting it (a ReplicaSet respawns it) or by triggering a rolling update. - Volume mounts without
subPath: Kubernetes updates the mounted files automatically. The kubelet's sync period (default--sync-frequency=1mon most managed clusters, configurable viakubelet-config) means changes propagate within roughly 60 seconds. The application still needs to watch the file for changes — a process that reads its config only once at startup gains nothing from this. - Volume mounts with
subPath: Changes are not propagated. This is a known Kubernetes limitation. Treat these mounts as static — the same as environment variables.
kubectl rollout restart deployment/my-api. For GitOps workflows (ArgoCD, Flux), the cleanest approach is to annotate the Deployment with a hash of the ConfigMap content — the Helm sha256sum trick — so that a ConfigMap change automatically triggers a new rollout without manual intervention.Immutable ConfigMaps
Kubernetes 1.21+ supports marking a ConfigMap as immutable by setting immutable: true in the manifest. Immutable ConfigMaps cannot be updated — you must delete and recreate them. In exchange, Kubernetes stops watching them for changes, which measurably reduces API server and kubelet load at large scale (clusters with tens of thousands of ConfigMaps). Google SRE teams at Kubernetes scale use immutable ConfigMaps as a practice: configuration changes are version-bumped (e.g., app-config-v42 → app-config-v43) and Deployments are updated to reference the new name. This pattern gives you full configuration history in Git, instant rollback by reverting the Deployment reference, and zero risk of a live ConfigMap being accidentally modified.
{app}-{component}-{env} (e.g., payments-api-production) and store them in a dedicated namespace per environment. Combined with RBAC that grants the payments ServiceAccount only get access to ConfigMaps in the payments namespace, you achieve least-privilege configuration access — a Pod cannot read another team's ConfigMap even if it tries.