ConfigMaps and Secrets
The idea in one sentence
Section titled “The idea in one sentence”ConfigMaps and Secrets externalize configuration data out of a container image so the same image can run in different environments, and a Secret is only base64-encoded by default — not encrypted.
ConfigMap: non-confidential configuration
Section titled “ConfigMap: non-confidential configuration”A ConfigMap holds key-value configuration data — feature flags, log levels, URLs — that a Pod can consume three ways: as environment variables, as command-line arguments, or as mounted files. Baking configuration into the image means rebuilding it for every environment; a ConfigMap lets one image read different configuration depending on which ConfigMap it is pointed at.
apiVersion: v1kind: ConfigMapmetadata: name: app-configdata: LOG_LEVEL: "info" FEATURE_FLAG_NEW_UI: "true"immutable: trueSetting immutable: true tells Kubernetes this ConfigMap’s data will never change again. That is a real optimization on large clusters: the kubelet no longer needs to keep watching the object for updates, which reduces load on the API server, and it also protects the ConfigMap from accidental edits.
Secret: same shape, sensitive intent — with a critical caveat
Section titled “Secret: same shape, sensitive intent — with a critical caveat”A Secret is structurally the same object as a ConfigMap, but it is meant for sensitive values such as passwords, tokens, and certificates. Here is the fact that matters most: a Secret’s data values are only base64-encoded, not encrypted. Base64 is an encoding, not a cipher — anyone with API read access to the Secret, or direct read access to the underlying etcd store, can decode it in one command. Do not treat a Secret as confidential by itself.
apiVersion: v1kind: Secretmetadata: name: db-credentialstype: Opaquedata: password: cGFzc3dvcmQxMjM=# Anyone with read access can trivially reverse the encoding — this is not encryptionkubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decodeReal confidentiality requires additional measures layered on top of the Secret object itself:
- Enabling encryption at rest for etcd, so the stored bytes are actually encrypted on disk.
- RBAC that tightly restricts who and what can
get/listSecrets. - An external secret manager (such as a cloud KMS-backed secrets store) integrated via a CSI driver or an operator, instead of relying on the raw Kubernetes Secret object as the source of truth.
Kubernetes ships a few built-in Secret types for common cases:
Opaque— the generic default, arbitrary key-value data.kubernetes.io/dockerconfigjson— registry credentials, used byimagePullSecretswhen pulling images from a private registry.kubernetes.io/tls— a TLS certificate and private key pair, typically consumed by an Ingress controller.
Consuming ConfigMaps and Secrets in a Pod
Section titled “Consuming ConfigMaps and Secrets in a Pod”apiVersion: v1kind: Podmetadata: name: webspec: containers: - name: web image: my-web-app:2.4 envFrom: - configMapRef: name: app-config env: - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: app-configThe two consumption paths behave differently when the source object changes after the Pod is already running:
- Environment variables are resolved once, at container start. Updating the ConfigMap or Secret afterward has no effect on an already-running container — it keeps the old value until it is restarted.
- Mounted volumes are periodically synced by the kubelet: when the source ConfigMap or Secret changes, the file’s contents on disk are eventually updated in place. The application itself still has to notice the file changed and reload it — Kubernetes does not restart the container for you.
flowchart LR cm["ConfigMap: app-config"] -->|envFrom| pod["Pod container"] cm -->|volume mount, kubelet syncs on change| pod sec["Secret: db-credentials"] -->|env valueFrom, fixed at start| pod sec -->|volume mount, kubelet syncs on change| pod