Env vs Volume Config

Mounting Configuration as Environment Variables Versus Volumes

envFrom turns every key into an environment variable, and env[].valueFrom.configMapKeyRef picks single keys. A configMap volume turns every key into a file. The difference shows when the ConfigMap changes:

The same change seen through an environment variable and a mounted fileShell
kubectl apply -f - >/dev/null <<'EOF'
apiVersion: v1
kind: Pod
metadata: { name: cfg-demo }
spec:
  containers:
  - name: shell
    image: localhost:33500/booknest-web:1.3
    command: [sleep, "3600"]
    envFrom: [{ configMapRef: { name: demo-literal } }]
    volumeMounts: [{ name: settings, mountPath: /etc/booknest }]
  volumes: [{ name: settings, configMap: { name: demo-dir } }]
EOF
kubectl wait --for=condition=Ready pod/cfg-demo >/dev/null
kubectl exec cfg-demo -- sh -c 'echo "env: $LOG_LEVEL"; ls -la /etc/booknest | tail -n +4'
kubectl patch configmap demo-literal -p '{"data": {"LOG_LEVEL": "debug"}}' >/dev/null
kubectl patch configmap demo-dir -p '{"data": {"log-level": "debug\n"}}' >/dev/null
S=$SECONDS; until kubectl exec cfg-demo -- grep -q debug /etc/booknest/log-level 2>/dev/null; do
  sleep 2; done
echo "file: after $((SECONDS - S)) s; env: $(kubectl exec cfg-demo -- printenv LOG_LEVEL)"
Output
env: info
drwxr-xr-x    2 root     root          4096 Sep 25 16:01 ..2026_09_25_16_01_55.1695178277
lrwxrwxrwx    1 root     root            32 Sep 25 16:01 ..data ->
  ..2026_09_25_16_01_55.1695178277
lrwxrwxrwx    1 root     root            16 Sep 25 16:01 log-level -> ..data/log-level
lrwxrwxrwx    1 root     root            16 Sep 25 16:01 page-size -> ..data/page-size
file: after 69 s; env: info

Environment variables are fixed at process start; a Deployment sees new values only after kubectl 5,150 rollout restart or a changed checksum annotation in its Pod template (Helm 29,435 's pattern, Packaging BookNest with Helm). Files change: the kubelet writes each version into a timestamped directory and swaps the ..data symlink atomically, after its sync period plus cache TTL (over a minute here). A subPath mount never updates, and the application must reread.