ConfigMaps & Secrets
ConfigMaps hold non-sensitive configuration and Secrets hold sensitive values such as passwords and tokens. Learn how to inject both via environment variables and volume mounts, and how updates propagate.
What is this?
ConfigMaps and Secrets both decouple configuration from container images so the same image runs everywhere with different settings. A ConfigMap stores non-sensitive data such as URLs and log levels; a Secret stores sensitive data such as passwords and API tokens, encoded as base64 in etcd (not encrypted by default). Both are consumed through environment variables, command arguments or volume mounts.
Why do you need it?
- The same container image can behave differently per environment without a rebuild.
- Config is versioned, reviewed and rolled back like code instead of baked into images.
- Secrets keep credentials out of images, repos and registry layers.
- Volume-mounted config updates propagate to running pods without redeploying the image.
Mental model
A form you fill out for each office
A ConfigMap
The form (config) is separate from the office building (image). Every office fills in its own values — the head office address, the branch phone number — while the building stays identical.
How it works
- You create a ConfigMap or Secret imperatively or from a manifest; Kubernetes stores it in etcd.
- A pod references keys via env (valueFrom.configMapKeyRef / secretKeyRef), envFrom, or a volume mount.
- At container start, kubelet injects the referenced values into the container's environment or filesystem.
- Volume-mounted ConfigMaps are updated periodically (roughly every minute) when the source changes.
- Environment variables are snapshotted at start and do not change until the pod restarts.
How config reaches a container and when it updates.
Key commands
kubectl create configmap app-config --from-literal=LOG_LEVEL=info --from-literal=MAX_RETRIES=3Create a ConfigMap from key=value pairs.
kubectl create configmap app-config --from-file=config.properties --from-file=./config-dir/Create a ConfigMap from file contents.
kubectl create secret generic db-secret --from-literal=DB_PASSWORD=supersecretCreate an Opaque Secret from a literal value.
kubectl get secret db-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -dDecode the base64 Secret value back to plaintext.
kubectl get configmap app-config -o yamlInspect a ConfigMap's data keys.
kubectl create configmap app-config --from-literal=LOG_LEVEL=debug --dry-run=client -o yaml | kubectl apply -f -Update a ConfigMap declaratively from new literals.
Examples
ConfigMap and Secret injected into a pod
apiVersion: v1kind: ConfigMapmetadata: name: app-configdata: LOG_LEVEL: info MAX_RETRIES: "3"---apiVersion: v1kind: Secretmetadata: name: app-secretstype: Opaquedata: DB_PASSWORD: c3VwZXJzZWNyZXQ=---apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: busybox:1.36 command: ["sleep", "3600"] env: - name: LOG_LEVEL valueFrom: configMapKeyRef: name: app-config key: LOG_LEVEL - name: DB_PASSWORD valueFrom: secretKeyRef: name: app-secrets key: DB_PASSWORD volumeMounts: - name: config-vol mountPath: /etc/config volumes: - name: config-vol configMap: name: app-configThe pod reads LOG_LEVEL from the ConfigMap and DB_PASSWORD from the Secret into its environment. It also mounts the whole ConfigMap at /etc/config, so each key becomes a file. The Secret value c3VwZXJzZWNyZXQ= is the base64 encoding of supersecret.
In production
- Environment-specific config (URLs, feature flags, log levels) lives in ConfigMaps, not in images.
- Credentials, tokens and registry auth (dockerconfigjson) live in Secrets referenced by pods.
- GitOps pipelines generate Secrets from a vault or secrets manager instead of committing them.
- Immutable ConfigMaps and Secrets prevent accidental mutation of configuration in shared namespaces.
Best practices
- Put only non-sensitive data in ConfigMaps; anything confidential belongs in a Secret or an external vault.
- Consume Secrets as volume mounts or files rather than environment variables when security matters.
- Set immutable: true for ConfigMaps and Secrets that should never change after creation.
- Use RBAC to restrict get/list on Secrets to the few users and workloads that need them.
Common mistakes
- Storing passwords in ConfigMaps or plain env values visible in the manifest.
- Assuming base64 encoding is encryption — it is trivially reversible.
- Relying on env-var injection for live config changes; environment does not update on change.
- Committing Secrets to git because they are base64, then leaking credentials.
Common issues
CreateContainerConfigError: configmap 'app-config' not foundThe referenced ConfigMap does not exist in the pod's namespace.
Create the ConfigMap or fix the configMapKeyRef name, then recreate the pod.
CreateContainerConfigError: secret 'x' not foundThe Secret is missing, renamed, or the key does not exist.
Verify with kubectl get secret and kubectl get secret x -o yaml, then fix the secretKeyRef.
Config file changes are not reflected in the podValues were injected as environment variables, which are fixed at container start.
Switch to a volume mount (updates propagate within a minute or two) or restart the pod.
Debugging tips
- kubectl get secret <name> -o jsonpath='{.data.KEY}' | base64 -d decodes a value for verification.
- kubectl describe pod <name> events reveal CreateContainerConfigError with the missing key name.
- For mounted config, kubectl exec <pod> -- ls /etc/config and cat files to verify content.
- kubectl get configmap <name> -o yaml confirms which keys actually exist.
Interview checkpoint
Hands-on lab
- Create ConfigMaps and Secrets from literals
- Inject values via environment variables
- Mount ConfigMap keys as files
- Observe how updates propagate to mounts but not env vars
Manage Config with ConfigMaps and Secrets
30 min · intermediate
Quick quiz
How is Secret data encoded in etcd?
Environment variables from a ConfigMap update automatically when the ConfigMap changes.
Which command creates a Secret from a key=value pair?
Cheat sheet
kubectl create configmap c --from-literal=K=VConfigMap from literalkubectl create configmap c --from-file=fConfigMap from filekubectl create secret generic s --from-literal=K=VOpaque Secretkubectl create secret tls t --cert=crt --key=keyTLS Secretkubectl get secret s -o yamlInspect encoded datakubectl get secret s -o jsonpath='{.data.K}' | base64 -dDecode a value