intermediate~3h

Configuration Management: ConfigMaps, Secrets & the Downward API

Hardcoding a database URL or an API key into a container image means rebuilding the image just to change an environment's config. This chapter is Kubernetes' answer: three distinct mechanisms for injecting configuration and identity information into a running Pod without ever baking it into the image.

A ConfigMap stores non-sensitive configuration data (URLs, feature flags, config file contents) as key-value pairs, decoupled from the container image — the same image can run in dev, staging, and production, each with its own ConfigMap supplying different values.

A Secret stores the exact same SHAPE of data (key-value pairs) but is intended for sensitive values (passwords, API keys, TLS certificates) — Kubernetes handles Secrets slightly differently at the API and storage level, though by default they are only base64-ENCODED, not encrypted, a distinction covered fully in Common Mistakes below.

The Downward API is a third, different mechanism: it exposes information about the Pod ITSELF (its own name, namespace, labels, resource limits) to the container running inside it, without that container needing to call the Kubernetes API to find out.

  • A ConfigMap or Secret can be consumed by a Pod in three ways: as environment variables (injected at container start), as mounted files (each key becomes a file in a mounted volume), or read directly via the Kubernetes API from within the Pod (rare, requires RBAC permissions).

  • When mounted as a volume, a ConfigMap/Secret update is eventually reflected in the mounted files automatically (via kubelet's periodic sync) WITHOUT restarting the Pod — but environment-variable-injected values are fixed at container start and never update until the Pod restarts.

  • Secrets are stored in etcd (the same cluster datastore covered in the Core Components chapter) base64-encoded by default — base64 is an ENCODING, not encryption, meaning anyone with etcd read access (or a kubectl get secret -o yaml) can trivially decode it back to plaintext.

  • The Downward API works through two mechanisms: environment variables populated from fieldRef/resourceFieldRef (like the Pod's own name or its CPU limit), or a volume mount that exposes the same information as files, updated live if the underlying value (like labels) changes.

  • Create a ConfigMap from literal values: kubectl create configmap app-config --from-literal=LOG_LEVEL=debug

  • Reference it as an environment variable in a Pod spec: envFrom: - configMapRef: { name: app-config }

  • Create a Secret similarly: kubectl create secret generic db-creds --from-literal=password=s3cr3t

  • Mount it as a file instead of an env var for anything sensitive: volumes: - name: creds secret: { secretName: db-creds }, then volumeMounts: - name: creds mountPath: /etc/secrets

  • Use the Downward API to expose the Pod's own name to itself: env: - name: POD_NAME valueFrom: { fieldRef: { fieldPath: metadata.name } }

A team runs the identical container image across dev/staging/production, with each environment's Kubernetes namespace supplying a differently-configured ConfigMap for that environment's database URL and feature flags — zero image rebuilds needed to promote a change from staging to production once the image itself is verified working.

A logging sidecar container (from the earlier Pods chapter's sidecar pattern) uses the Downward API to tag every log line with the Pod's own name and namespace, without the sidecar needing any Kubernetes API permissions to look that information up itself.

A production cluster integrates an external secrets manager (HashiCorp Vault, AWS Secrets Manager) via a controller that syncs secrets INTO native Kubernetes Secret objects, specifically because native Secrets' base64-only protection isn't sufficient for a compliance-regulated workload.

  • Mount Secrets as files, not environment variables, wherever practical — environment variables are more likely to leak into logs, error messages, or child-process environments than a file only the application explicitly reads.

  • Enable encryption-at-rest for Secrets in etcd (EncryptionConfiguration at the API server level) — this is NOT on by default in most cluster setups, and base64 alone is not meaningfully protecting anything from someone with etcd access.

  • Use a dedicated external secrets manager for anything genuinely sensitive in a production, compliance-relevant workload — native Kubernetes Secrets are adequate for many cases but were never designed as a full secrets-management product.

  • Namespace ConfigMaps/Secrets per environment (or per team) rather than sharing one global set across a whole cluster, so a change intended for staging can't accidentally affect production because they happened to reference the same object.

Assuming a Kubernetes Secret is encrypted just because it's called 'Secret'

— by default it's base64-encoded only, which is trivially reversible; treating it as equivalent to a properly encrypted credential store is a common, real security gap.

Expecting an environment-variable-injected ConfigMap value to update live

— only volume-mounted ConfigMaps/Secrets update without a Pod restart; env-var-injected ones are frozen at container start, a frequent source of 'I updated the config, why didn't anything change' confusion.

Committing a Secret manifest (even base64-encoded) to a Git repository

— base64 is not encryption, and a 'secret' committed to version control is exposed to everyone with repo access, plus permanently present in Git history even after deletion.

Putting genuinely large configuration (an entire application config file) directly in environment variables

— most shells and some container runtimes have real size limits on environment variables; large configuration belongs in a mounted ConfigMap file, not squeezed into env vars.

  • Mounted ConfigMap/Secret volumes avoid the (small but real) overhead of the kubelet re-injecting environment variables on every container restart, and allow an application to watch the mounted file for changes instead of requiring a full Pod restart for every config change.

  • Keep ConfigMaps reasonably small and focused (per-service, not one giant cluster-wide ConfigMap) — an oversized ConfigMap adds real latency to every Pod's startup as kubelet has to fetch and mount the whole thing regardless of how much of it any single Pod actually uses.

  • Enable etcd encryption-at-rest for Secrets as a non-negotiable production baseline, not an optional hardening step — this closes the base64-is-not-encryption gap directly.

  • Adopt an external secrets manager with a Kubernetes-native sync controller for any workload under real compliance requirements (PCI, HIPAA, SOC2) — native Secrets alone typically won't satisfy an audit.

  • Use RBAC (covered in the Security chapter) to tightly restrict which service accounts/users can read Secret objects — a ConfigMap leaking is inconvenient; a Secret leaking is a real incident.

  • Version and code-review ConfigMap/Secret changes through the same GitOps pipeline as application manifests (using sealed-secrets or an external-secrets operator to keep the actual sensitive VALUES out of Git while the reference/structure stays version-controlled).

  • Create a ConfigMap and mount it as an environment variable in one Pod, and as a volume in another — update the ConfigMap's value and observe that only the volume-mounted Pod picks up the change without a restart.

  • Create a Secret, retrieve it with kubectl get secret my-secret -o yaml, and manually base64-decode the value from the output to confirm firsthand that it's an encoding, not encryption.

  • Add a Downward-API-sourced environment variable exposing a Pod's own resource limits to itself, and print it from inside the running container to confirm the value matches the Pod spec.

ConfigMaps and Secrets both decouple configuration from the container image, letting one image run correctly across every environment — the real distinction between them is intent (sensitive vs. not) and Kubernetes' handling (base64-only by default for Secrets, which is NOT encryption and needs etcd encryption-at-rest or an external secrets manager for real production security). The Downward API is a separate mechanism entirely, exposing a Pod's own metadata to itself without any Kubernetes API calls needed from inside the container.

Want a visual for this concept?

Generate a diagram tailored to “Configuration Management: ConfigMaps, Secrets & the Downward API” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Resource Management: Requests, Limits, QoS Classes & OOMKilled← Back to all Kubernetes chapters