ConfigMaps and Secrets: Configuration Best Practices in Kubernetes
Twelve-factor apps keep configuration out of the build. In Kubernetes that means ConfigMaps for non-sensitive settings and Secrets for credentials. They look almost identical, which is exactly why teams misuse them. This is how to use each well.
ConfigMaps: everything that is not a secret
Feature flags, log levels, service URLs, tuning parameters — anything you would happily print to a log belongs in a ConfigMap.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
CACHE_TTL_SECONDS: "300"
app.properties: |
timeout=30s
retries=3
Secrets: the same shape, stricter handling
Secrets store passwords, API keys, and certificates. Critically, base64 is encoding, not encryption — anyone with read access sees the value. Treat Secrets as sensitive at the RBAC and storage level.
kubectl create secret generic db-creds \
--from-literal=username=app \
--from-literal=password='s3cr3t'
Two ways to consume them
As environment variables
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: db-creds
Simple, but env vars are visible in describe, child processes, and crash dumps — and they do not update without a Pod restart.
As mounted files
volumes:
- name: config
configMap:
name: app-config
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
Mounted ConfigMaps and Secrets are updated in place by the kubelet (with a short delay), so a watching app can pick up changes without a restart. Prefer files for anything you may rotate.
Best practices
1. Separate config from secrets, always
Never stuff a password into a ConfigMap because it was convenient. The whole point of the split is that Secrets get tighter RBAC, encryption at rest, and audit attention.
2. Enable encryption at rest
By default Secrets sit in etcd base64-encoded. Configure an EncryptionConfiguration on the API server (or use a managed provider) so a stolen etcd snapshot is not a breach.
3. Lock down RBAC
Grant get/list on Secrets narrowly. Many incidents are over-broad ServiceAccount permissions, not exotic exploits.
kubectl auth can-i list secrets --as=system:serviceaccount:app:default -n app
4. Keep secrets out of Git
Plain Secret manifests in a repo defeat the purpose. Use Sealed Secrets, an external secrets operator backed by a vault, or your cloud's secret manager — so Git holds an encrypted reference, never the value.
5. Plan for rotation
Mounted Secrets update live, but most apps cache credentials at startup. Either watch the mounted file and reload, or trigger a rolling restart when a Secret changes:
kubectl rollout restart deployment/api
6. Version your config changes
Treat ConfigMaps like code: review changes, and consider immutable, hash-suffixed ConfigMaps so a config change forces a clean rollout you can roll back.
Immutable ConfigMaps and Secrets
Marking config immutable improves performance and prevents accidental edits. You replace rather than mutate, which pairs naturally with a fresh rollout per change:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v7
immutable: true
data:
LOG_LEVEL: "info"
Keeping it visible
Misconfiguration is the quiet cause behind many crash loops — a Pod mounted the wrong Secret, or an env var never got set. Inspecting which ConfigMaps and Secrets a Pod actually consumed, and confirming the keys are present, is far faster in a visual tool like Kubexer than decoding base64 by hand across several commands.
Wrapping up
Split sensitive from non-sensitive, prefer file mounts for anything rotatable, encrypt Secrets at rest, keep them out of Git, and treat config changes as versioned rollouts. Configuration discipline prevents more outages than almost any other practice.