Configuration: ConfigMaps and Secrets
Keep settings and credentials out of your images, inject them into Pods, and avoid the common traps.
If a database URL or a log level is baked into your container image, changing it means rebuilding, pushing, and redeploying the image. Kubernetes separates configuration from images with two objects: ConfigMaps for ordinary settings and Secrets for sensitive values. This guide shows how to create both, how to get them into a Pod, and what Secrets do and don’t protect.
Create a ConfigMap
A ConfigMap is a set of key-value pairs. Values can be short strings or entire files:
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
LOG_LEVEL: info
API_URL: http://api.internal:8080
motd.txt: |
Welcome to the demo environment
You can also create one from the command line, which is handy for files:
kubectl create configmap web-config \
--from-literal=LOG_LEVEL=info \
--from-file=motd.txt
kubectl describe configmap web-config
ConfigMaps are for small, non-sensitive data (the limit is 1 MiB), and they belong to a namespace. A Pod can only use ConfigMaps from its own namespace.
Use it as an environment variable or a file
The same Pod can consume a ConfigMap both ways:
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: nginx:1.25
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: web-config
key: LOG_LEVEL
volumeMounts:
- name: config
mountPath: /usr/share/nginx/html/motd.txt
subPath: motd.txt
volumes:
- name: config
configMap:
name: web-config
Two things are happening here. The env entry copies one key into an environment variable. (envFrom can import every key at once, though keys that aren’t valid variable names get skipped.) The volumes and volumeMounts entries mount the ConfigMap as files, one file per key.
If you mount the whole ConfigMap at a directory like this, the directory is replaced by the mounted files:
volumeMounts:
- name: config
mountPath: /etc/web
That is fine for a directory you own, but it hides anything already at that path. Mounting over an existing config directory, such as /etc/nginx, can break the application, and the container may crash-loop with a “file not found” error. That is what subPath is for, as in the first example: it mounts a single key as a single file and leaves the rest of the directory alone. The trade-off is that files mounted with subPath do not receive later updates to the ConfigMap.
Verify what landed in the container:
kubectl exec web -- ls /usr/share/nginx/html
kubectl exec web -- env | grep LOG_LEVEL
Store credentials in a Secret
A Secret looks like a ConfigMap but is meant for passwords, tokens, and keys:
kubectl create secret generic db-credentials \
--from-literal=username=app \
--from-literal=password='S3cr3t-value'
In YAML, use stringData to write plain text and let Kubernetes encode it:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
username: app
password: S3cr3t-value
If you write the encoded data field yourself, encode with echo -n 'value' | base64. Without -n, the trailing newline becomes part of the password and authentication fails in confusing ways. Consume a Secret the same way as a ConfigMap:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
Avoid committing Secret manifests with real values to Git. Tools such as Sealed Secrets, External Secrets Operator, and SOPS exist so you can keep encrypted or referenced secrets in a repository.
Know what Secrets do and don’t protect
Base64 is an encoding, not encryption. Anyone who can read the Secret through the API can decode it:
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
By default Secrets are also stored unencrypted in etcd. To actually protect them:
- Restrict who can read Secrets with RBAC, and remember that anyone who can create a Pod in a namespace can mount its Secrets.
- Enable encryption at rest for etcd, or use a cloud provider’s key management.
- Consider an external secret manager if your requirements are strict.
Environment variables are convenient but easy to leak, since they show up in kubectl exec ... env, in crash dumps, and sometimes in logs. Mounting a Secret as a file is often a better choice for sensitive values.
Update configuration on purpose
Environment variables are read once, when the container starts, so a changed ConfigMap or Secret doesn’t reach running Pods. Restart them to pick up the change:
kubectl rollout restart deployment/web
Files from a whole-directory mount do refresh eventually, but many applications only read their config at startup anyway. For automated setups, make the Pod template change whenever the configuration does. That is what the config-hash techniques in the Argo CD Helm and Kustomize guide are for.
Popular Guides
-
What Kubernetes Is and the Problems It Solves Why containers alone stop scaling, and how Kubernetes turns a group of machines into one platform. -
How a Kubernetes Cluster Works: Control Plane and Nodes The components that make a cluster tick, and what happens between kubectl apply and a running container. -
Deployments: Scaling, Self-Healing, and Rolling Updates Run replicas of a Pod, update them without downtime, and roll back when a release goes wrong. -
Services: Stable Networking for Ephemeral Pods Give Pods a stable address, expose them inside and outside the cluster, and restrict who can talk to whom. -
Debug a Failing Pod: describe, logs, exec, and port-forward A repeatable checklist for finding out why a Pod is Pending, crashing, or not answering.