ConfigMaps & Secrets
TL;DR
Use ConfigMaps for non-sensitive configuration and Secrets for sensitive values. Both can be consumed as environment variables or mounted files. Environment variables do not update in a running container; mounted ConfigMap/Secret volumes update eventually, but many apps still need a restart or reload signal.
When To Use
| Object | Use For | Do Not Use For |
|---|---|---|
| ConfigMap | Non-sensitive app config, feature flags, config files. | Passwords, tokens, private keys. |
| Secret | Credentials, certificates, API keys, registry credentials. | Large files, ordinary config, anything that belongs in external secret managers. |
Daily Commands
bashconfig-secret-ops.sh
# List configuration objects in a namespace.
kubectl get configmap,secret -n <namespace>
# Inspect metadata and keys. Secret values are hidden by describe.
kubectl describe configmap <configmap> -n <namespace>
kubectl describe secret <secret> -n <namespace>
# Print a ConfigMap as YAML.
kubectl get configmap <configmap> -n <namespace> -o yaml
# Decode one Secret key. Requires permission to read secrets.
kubectl get secret <secret> -n <namespace> -o jsonpath='{.data.password}' | base64 --decode; echo
# Create examples from literals.
kubectl create configmap app-config -n app --from-literal=LOG_LEVEL=info --from-literal=FEATURE_X=true
kubectl create secret generic app-secret -n app --from-literal=username=app --from-literal=password='REPLACE_ME'ConfigMap Patterns
yamlconfigmap-env-and-file.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
namespace: app
data:
LOG_LEVEL: info # Good for env var usage.
FEATURE_X_ENABLED: "true" # ConfigMap values are strings.
nginx.conf: | # Good for mounted file usage.
server {
listen 8080;
location /healthz { return 200; }
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: app
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
envFrom:
- configMapRef:
name: web-config # Imports simple keys like LOG_LEVEL as env vars.
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf # Mount one key as one file.
volumes:
- name: nginx-config
configMap:
name: web-configSecret Patterns
Security reminderKubernetes Secrets are base64-encoded, not automatically encrypted for every environment. Production clusters should enable encryption at rest and restrict Secret reads with RBAC.
yamlsecret-stringdata.yaml
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
namespace: app
type: Opaque
stringData:
username: app_user # stringData accepts plain text and Kubernetes stores it under data as base64.
password: CHANGE_ME # Prefer sealed/external secret workflows in GitOps.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
namespace: app
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: registry.example.com/api:1.0.0
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: database-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: database-credentials
key: passwordVolume Mount Patterns
Use one mount per key via subPath when you must place a file at an existing directory without shadowing sibling files. Project whole ConfigMaps using items or default mode to control permissions on disk.
| Pattern | Best for | Watch outs |
|---|---|---|
| Entire volume mount at emptyDir path | Many keys consumed as files tree. | Overwrites mountpoint; ensure parent empty. |
subPath single key | Drop nginx.conf next to default files. | No kubelet refresh; mount static snapshot. |
projected volume | Mixed ConfigMap + Secret + downwardAPI. | Atomic updates when all sources rev together. |
envFrom | 12-factor style flags. | Requires rollout for value changes. |
yamlprojected-volume.yaml
volumes:
- name: all-in-one
projected:
sources:
- configMap:
name: app-config
- secret:
name: tls-inline
- downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labelsImmutable ConfigMaps
Marking immutable: true stops accidental hot patches and enables API server optimizations, but you must clone + roll out a new object (new name or versioned suffix) for every change.
yamlimmutable-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config-v3
namespace: app
immutable: true
data:
THEME: darksubPath Sharp Edges
- Rotation blindness: kubelet bind-mounts the inode at Pod start; rotated Secret data will not flow into an existing
subPathfile. - chmod limits: you cannot apply
defaultModeper key when using multiple subPath entries from the same volume in some patterns—test on target OS. - Copy-on-write surprises: editing the file in-container may diverge from projected source without obvious errors.
- Windows path casing: mismatched case between
mountPathand app expectations fails silently.
Reload Behavior
| Consumption Method | Runtime Update? | Operational Note |
|---|---|---|
| Environment variable | No | Restart or rollout Pods after ConfigMap/Secret change. |
| Mounted volume | Eventually | Kubelet updates files, but app must re-read or reload them. |
subPath mount | No | SubPath mounts do not receive live updates. |
| External secret operator | Depends | Check operator sync interval and rollout trigger pattern. |
bashrollout-after-config-change.sh
# Restart a Deployment so env var changes are picked up.
kubectl rollout restart deployment/<deployment> -n <namespace>
kubectl rollout status deployment/<deployment> -n <namespace>
# Show which Pods were recreated.
kubectl get pods -n <namespace> -l app=<app-label> -o wideTroubleshooting
- CreateContainerConfigError: often means referenced ConfigMap/Secret/key is missing.
- Wrong namespace: ConfigMaps and Secrets must be in the same namespace as the Pod that uses them.
- Invalid env key: keys imported through
envFrommust be valid environment variable names. - GitOps safety: avoid committing raw Secrets. Use Sealed Secrets, External Secrets, SOPS, or the client's approved secret flow.
bashconfig-debug.sh
kubectl describe pod <pod> -n <namespace>
kubectl get configmap,secret -n <namespace>
kubectl get pod <pod> -n <namespace> -o yaml | grep -A20 -E 'env:|envFrom:|volumes:|volumeMounts:'
kubectl get events -n <namespace> --sort-by=.lastTimestamp | grep -i -E 'configmap|secret|key|mount|CreateContainerConfigError'