Skip to content

ConfigMaps and Secrets

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.

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: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
FEATURE_FLAG_NEW_UI: "true"
immutable: true

Setting 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: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
password: cGFzc3dvcmQxMjM=
Terminal window
# Anyone with read access can trivially reverse the encoding — this is not encryption
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode

Real 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/list Secrets.
  • 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 by imagePullSecrets when pulling images from a private registry.
  • kubernetes.io/tls — a TLS certificate and private key pair, typically consumed by an Ingress controller.
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
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-config

The 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
ConfigMap and Secret data reaching a Pod as env vars and as a mounted volume
Is base64 encoding in a Kubernetes Secret a form of encryption
Which consumption method picks up an update to a ConfigMap or Secret without restarting the container
Why might you mark a ConfigMap as immutable
Which built-in Secret type is used to store credentials for pulling images from a private registry