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

ObjectUse ForDo Not Use For
ConfigMapNon-sensitive app config, feature flags, config files.Passwords, tokens, private keys.
SecretCredentials, 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-config

Secret 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: password

Volume 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.

PatternBest forWatch outs
Entire volume mount at emptyDir pathMany keys consumed as files tree.Overwrites mountpoint; ensure parent empty.
subPath single keyDrop nginx.conf next to default files.No kubelet refresh; mount static snapshot.
projected volumeMixed ConfigMap + Secret + downwardAPI.Atomic updates when all sources rev together.
envFrom12-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.labels

Immutable 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: dark

subPath Sharp Edges

  • !Rotation blindness: kubelet bind-mounts the inode at Pod start; rotated Secret data will not flow into an existing subPath file.
  • !chmod limits: you cannot apply defaultMode per 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 mountPath and app expectations fails silently.

Reload Behavior

Consumption MethodRuntime Update?Operational Note
Environment variableNoRestart or rollout Pods after ConfigMap/Secret change.
Mounted volumeEventuallyKubelet updates files, but app must re-read or reload them.
subPath mountNoSubPath mounts do not receive live updates.
External secret operatorDependsCheck 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 wide

Troubleshooting

  • !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 envFrom must 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'