ConfigMaps and Secrets Done Right
Introduction¶
ConfigMaps hold non-sensitive configuration; Secrets hold sensitive values. They have nearly identical APIs. The interesting parts are how pods consume them and what "Secret" actually protects.
Two ways to consume¶
As environment variables¶
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: db-password
- Simple, universal.
- Not updated while the pod runs — a changed ConfigMap needs a pod restart.
- Env vars can leak:
kubectl describe pod, crash dumps, child processes,/proc/<pid>/environ.
As mounted files¶
volumes:
- name: config
configMap:
name: app-config
- name: secrets
secret:
secretName: app-secrets
containers:
- volumeMounts:
- name: config
mountPath: /etc/app
- name: secrets
mountPath: /etc/app/secrets
readOnly: true
- Each key becomes a file.
- Auto-updated in the volume within ~a minute (unless
subPathis used —subPathmounts don't update). Your app still has to re-read the file. - Preferred for secrets: files aren't inherited by child processes, don't show
in
describe, can bereadOnly.
Secrets are base64, not encrypted¶
kubectl get secret app-secrets -o jsonpath='{.data.db-password}' | base64 -d
Anyone with get secret RBAC, or etcd access, reads them in plaintext. To
actually protect them:
- Encryption at rest — configure the API server with an
EncryptionConfiguration(aescbc/kms provider). Now etcd holds ciphertext. Cloud-managed clusters often enable this; verify. - RBAC —
get/liston Secrets is powerful; restrict it. Don't grantlist secretscluster-wide. - External secret managers — External Secrets Operator or the Secrets Store CSI driver pull from Vault / AWS Secrets Manager / GCP SM at pod start, so the source of truth is outside the cluster.
- Never commit Secret YAML — use Sealed Secrets (encrypts to a cluster-specific key, safe in git) or SOPS.
Rolling pods on config change¶
Kubernetes does not restart pods when a ConfigMap/Secret changes. Options:
- Put a hash of the config in a pod annotation so the Deployment's pod template
changes → rollout. Helm:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}. kubectl rollout restart deployment/appafter applying config.- Use a tool like Reloader (watches referenced ConfigMaps/Secrets, triggers rollouts).
- Mounted-file config + an app that reloads on file change (SIGHUP, inotify).
Immutable ConfigMaps/Secrets¶
immutable: true
Prevents accidental edits, and lets the kubelet stop watching them (less API
load at scale). You create a new one (app-config-v2) and update the reference.
Verification and troubleshooting¶
kubectl get cm app-config -o yaml
kubectl describe pod mypod | grep -A5 'Environment\|Mounts'
kubectl exec mypod -- env | grep DB_
kubectl exec mypod -- cat /etc/app/secrets/db-password
- Pod stuck
CreateContainerConfigError— the ConfigMap/Secret or a referenced key doesn't exist.kubectl describe podnames it. - Changed the ConfigMap, app still sees old values — env vars don't update
(restart needed), or it's a
subPathmount (never updates), or the app cached it at startup. - Mounted secret file has odd permissions — default is 0644; set
defaultMode: 0400on the volume for tighter perms. Note files are owned by root unlessfsGroupis set. base64value has a trailing newline —echo -nwhen creating, or usekubectl create secret generic ... --from-literal/--from-filewhich handle it.- Secret visible in
kubectl describe— you put it inenvinline or in a ConfigMap. Move tosecretKeyRefor a mounted volume. - App in CrashLoop reading config — the file exists but is a directory
(whole ConfigMap mounted where a single file was expected) — use
subPathoritems:to project one key.
Related tools and reading¶
- On-site: Base64 encoder/decoder, YAML validator.
- Related posts: Secrets in CI without leaking them, Debugging CrashLoopBackOff.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in Docker & Kubernetes
StatefulSets Explained
StatefulSets give pods sticky names, sticky storage, and ordered operations — what databases and clustered apps need. Here's how each guarantee works.
October 24, 2026Persistent Volumes and Claims, Explained
PVC is a request, PV is the fulfilled storage, StorageClass is the factory. Most 'Pending PVC' problems are an access-mode or provisioner mismatch.
October 24, 2026Kubernetes Jobs and CronJobs
A Job runs pods until they succeed; a CronJob creates Jobs on a schedule. Here's parallelism, retry limits, the missed-schedule trap, and history cleanup.
October 24, 2026