ConfigMaps for externalizing non-sensitive config, why a Kubernetes Secret is base64-encoded rather than encrypted by default, mounting as env vars vs files, and why editing a ConfigMap doesn't automatically restart anything.
Published September 23, 2026
apiVersion: v1
kind: ConfigMap
metadata: { name: app-config }
data:
LOG_LEVEL: "INFO"
FEATURE_FLAG_NEW_CHECKOUT: "true"
A ConfigMap holds non-sensitive configuration as key-value data, kept entirely separate from the container image itself — this is the same "externalize configuration, don't bake it into the artifact" principle behind Payment — Security's environment-based secrets and the general twelve-factor-app configuration philosophy: the same image should be deployable to staging and production with DIFFERENT config, without rebuilding it, simply by pointing it at a different ConfigMap.
apiVersion: v1
kind: Secret
data:
DB_PASSWORD: c3VwZXJzZWNyZXQ= # this is base64, NOT encryption — trivially reversible
This is a genuinely important, commonly-misunderstood point: a Kubernetes Secret object stores its values BASE64-ENCODED, which is an ENCODING for safe transport in YAML/JSON, not an ENCRYPTION for confidentiality — anyone with read access to the Secret object (via kubectl get secret -o yaml) can trivially decode it back to plaintext. Genuine protection requires encryption at rest, a separate CLUSTER-LEVEL configuration (encrypting Secret data as stored in etcd, the cluster's backing datastore) that must be explicitly enabled — a Secret object by itself, without that cluster-level setting, offers essentially no confidentiality protection beyond ordinary RBAC access control on who can read the object at all.
# As environment variables
envFrom:
- configMapRef: { name: app-config }
# As mounted files
volumes:
- name: config-volume
configMap: { name: }
{ , }
Both ConfigMaps and Secrets can be exposed to a container either as environment variables OR as files mounted into the container's filesystem. Environment variables are simpler and match how most applications already read configuration, but have a real limitation: they're typically only read ONCE, at process startup — updating them requires a pod restart. Mounted files, by contrast, DO update live inside a running pod when the underlying ConfigMap/Secret changes (Kubernetes updates the mounted file's content automatically), though the APPLICATION still needs to actually be watching that file for changes to pick up the update — mounting alone doesn't make an application hot-reload configuration it wasn't built to watch.
This is a subtle but important operational gap: changing a ConfigMap's data does NOT automatically trigger a rollout or restart of pods referencing it — for env-var-mounted config, this means running pods simply keep their OLD values until manually restarted; even for file-mounted config, only apps that actively watch the file for changes pick up the update live. A common workaround pattern is adding a CHECKSUM of the ConfigMap's content as an annotation on the pod template itself — since Kubernetes treats ANY pod template change (including an annotation) as a trigger for a rolling update (Deployments & Rollouts), a changed checksum forces a fresh rollout, indirectly ensuring pods restart and pick up the new config.
Q: If Secrets aren't encrypted by default, what's the actual production-grade answer for genuinely sensitive values? A: Beyond enabling cluster-level encryption-at-rest for Secret data, many production setups use an external secrets manager (AWS Secrets Manager, HashiCorp Vault) as the actual source of truth, with a controller syncing values INTO Kubernetes Secrets (or injecting them directly at runtime) — keeping the master secret genuinely encrypted and access-audited outside Kubernetes' own, more limited default protections.
Q: Is there ever a reason to prefer environment variables over mounted files for configuration, given files update live? A: Simplicity and broad compatibility — nearly every application/framework already reads config from environment variables with zero extra code, while consuming a mounted file for live-reload requires the application to explicitly implement file-watching; for config that genuinely never needs to change without a full redeploy anyway, the simpler env-var approach is often the pragmatic default.
Q: Does the checksum-annotation trick for forcing a rollout have any downsides? A: It means EVERY ConfigMap change (even a trivial one) triggers a full rolling update of every pod referencing it, which is a real cost for a config value that changes frequently — for high-churn configuration, an application designed to watch and hot-reload a mounted file directly (avoiding the restart entirely) is a more efficient long-term answer.
Q: How does this relate to the 'never log sensitive data' principle from Centralized Logging? A: Directly — a Secret's value ending up as a logged environment variable dump (many frameworks log their full environment at startup for debugging) would defeat the purpose of using a Secret at all; the same log-scrubbing discipline from Centralized Logging applies specifically to anything sourced from a Kubernetes Secret.