Skip to content

ConfigMaps & Secrets

Manage application configuration and sensitive data in Kubernetes.

Time: ~10 minutes Difficulty: Beginner

  • ConfigMaps: key-value pairs and file-based configuration
  • Secrets: storing sensitive data (passwords, TLS certs, registry auth)
  • Two injection methods: environment variables vs volume mounts
  • Immutable ConfigMaps for performance and safety
  • Secret types: Opaque, dockerconfigjson
  • The base64 “encoding is not encryption” gotcha

Navigate to the demo directory:

Terminal window
cd demos/configmaps-secrets
Terminal window
kubectl apply -f manifests/namespace.yaml
kubectl apply -f manifests/configmap-literal.yaml
kubectl apply -f manifests/configmap-file.yaml
kubectl apply -f manifests/configmap-immutable.yaml
kubectl apply -f manifests/secrets.yaml
kubectl apply -f manifests/pod-env-vars.yaml
kubectl apply -f manifests/pod-volume-mount.yaml

The env-demo pod injects ConfigMap and Secret data as environment variables:

Terminal window
kubectl logs env-demo -n config-demo

Output shows:

  • envFrom injects all keys from a ConfigMap at once
  • valueFrom.secretKeyRef injects individual Secret keys
  • valueFrom.configMapKeyRef injects individual ConfigMap keys
Terminal window
kubectl exec env-demo -n config-demo -- env | grep -E "APP_|DATABASE_|VERSION"

The nginx-configured Deployment mounts ConfigMaps and Secrets as files:

Terminal window
kubectl port-forward svc/nginx-configured 8080:80 -n config-demo

Open http://localhost:8080 to see the page served from the ConfigMap.

Terminal window
# ConfigMap mounted as nginx config
kubectl exec deploy/nginx-configured -n config-demo -- cat /etc/nginx/conf.d/default.conf
# Secret mounted as files (one file per key)
kubectl exec deploy/nginx-configured -n config-demo -- ls /etc/secrets/
kubectl exec deploy/nginx-configured -n config-demo -- cat /etc/secrets/DB_USER

The app-version ConfigMap is immutable. Try to update it:

Terminal window
kubectl patch configmap app-version -n config-demo \
--type=merge -p '{"data":{"VERSION":"2.0.0"}}'

This fails. Immutable ConfigMaps cannot be changed after creation. You must delete and recreate. This protects against accidental changes and reduces API server load (no watches needed).

Terminal window
# Secrets are base64 encoded, NOT encrypted
kubectl get secret db-credentials -n config-demo -o jsonpath='{.data.DB_PASSWORD}' | base64 -d

Anyone with access to the namespace can read Secrets. For real encryption at rest, enable EncryptionConfiguration or use an external secret store.

manifests/
namespace.yaml # config-demo namespace
configmap-literal.yaml # Key-value pairs (APP_ENV, APP_PORT, etc.)
configmap-file.yaml # Multi-line files (nginx.conf, index.html)
configmap-immutable.yaml # Immutable ConfigMap (cannot be updated)
secrets.yaml # Two Secret types (Opaque, dockerconfigjson)
pod-env-vars.yaml # Pod injecting config as env vars
pod-volume-mount.yaml # Deployment mounting config as files

When to use env vars vs volume mounts:

MethodBest ForLimitation
Env varsSimple key-value pairsNot updated after pod start
Volume mountsConfig files, certs, multi-line dataAdds filesystem complexity

Volume-mounted ConfigMaps can update automatically when the ConfigMap changes (with a delay), but only if mounted as a directory. Mounts using subPath do NOT auto-update. Env vars never update, the pod must be restarted.

  1. Update the ConfigMap and restart the pod to pick up changes (this demo uses subPath mounts, which require a restart):

    Terminal window
    kubectl patch configmap nginx-config -n config-demo \
    --type=merge -p '{"data":{"index.html":"<h1>Updated!</h1>"}}'
    # subPath mounts require a pod restart to pick up changes
    kubectl rollout restart deploy/nginx-configured -n config-demo
    kubectl rollout status deploy/nginx-configured -n config-demo
    # Verify the update
    kubectl exec deploy/nginx-configured -n config-demo -- cat /usr/share/nginx/html/index.html
  2. Create a Secret from the command line:

    Terminal window
    kubectl create secret generic api-key \
    --from-literal=key=my-api-key-12345 -n config-demo
    kubectl get secret api-key -n config-demo -o yaml
  3. Create a ConfigMap from a file:

    Terminal window
    echo '{"debug": true}' > /tmp/config.json
    kubectl create configmap json-config \
    --from-file=/tmp/config.json -n config-demo
    kubectl get configmap json-config -n config-demo -o yaml
Terminal window
kubectl delete namespace config-demo

See docs/deep-dive.md for a detailed explanation of ConfigMap/Secret internals, hot-reload mechanics, subPath gotchas, Secret encryption at rest, external secret operators, and projection volumes.

Move on to Probes & Lifecycle to learn about health checks and graceful shutdown.