3.4 ConfigMaps, Secrets & Application Configuration

Key Takeaways

  • ConfigMaps hold non-confidential key-value configuration data so that the same immutable container image can run unchanged across development, staging, and production.
  • Secrets hold small amounts of sensitive data and are base64-encoded, not encrypted — encryption at rest in etcd must be enabled separately by the cluster administrator.
  • ConfigMaps and Secrets can be consumed as environment variables or mounted as volumes; only volume mounts pick up updates without a Pod restart.
  • Both objects are namespaced and capped at roughly 1 MiB, which is why large payloads belong in a PersistentVolume or an external store rather than a ConfigMap.
  • Common Secret types include Opaque (the default), kubernetes.io/dockerconfigjson for registry credentials, kubernetes.io/tls for certificate pairs, and kubernetes.io/service-account-token.
Last updated: August 2026

3.4 ConfigMaps, Secrets & Application Configuration

Quick Answer: A ConfigMap stores non-confidential configuration as key-value pairs; a Secret stores small amounts of sensitive data such as passwords, tokens, and TLS keys. Both are namespaced objects capped at about 1 MiB, and both can be injected into a Pod either as environment variables (read once at container start) or as a mounted volume (updated in place, without a restart). Secrets are base64-encoded, not encrypted — encryption at rest is a separate cluster-level setting.

The Twelve-Factor App rule "store config in the environment" is not an abstract principle in Kubernetes: it is an API. Building one immutable image and injecting environment-specific values at runtime is the entire reason ConfigMaps and Secrets exist, and KCNA tests both the mechanism and its security limits.


1. Why Configuration Must Leave the Image

If a database URL is baked into a container image, you need a different image per environment. That breaks immutability, breaks promotion ("the exact artifact I tested is the artifact I ship"), and breaks rollback. Kubernetes solves it by keeping configuration in cluster objects and binding it to the Pod at scheduling time.

   ONE IMAGE                        THREE ENVIRONMENTS
  myapp:v2.1.0  ──┬──► dev     + ConfigMap/dev-config     + Secret/dev-db
                  ├──► staging + ConfigMap/staging-config + Secret/staging-db
                  └──► prod    + ConfigMap/prod-config    + Secret/prod-db

2. ConfigMaps

A ConfigMap is a namespaced API object holding key-value pairs. Values may be short strings or entire file contents (an nginx.conf, a application.properties, a JSON policy document).

apiVersion: v1
kind: ConfigMap
metadata:
  name: web-config
  namespace: production
data:
  LOG_LEVEL: "info"
  FEATURE_CHECKOUT_V2: "true"
  nginx.conf: |
    server {
      listen 8080;
      location /healthz { return 200; }
    }

Consuming a ConfigMap

There are three consumption patterns, and the difference between them is a favourite exam distinction:

  1. Single value as an environment variablevalueFrom.configMapKeyRef pulls one named key.
  2. All keys as environment variablesenvFrom.configMapRef imports every key at once.
  3. Mounted as a volume — each key becomes a file whose name is the key and whose contents are the value.
spec:
  containers:
  - name: web
    image: myapp:v2.1.0
    env:
    - name: LOG_LEVEL                 # pattern 1: one key
      valueFrom:
        configMapKeyRef:
          name: web-config
          key: LOG_LEVEL
    envFrom:
    - configMapRef:                   # pattern 2: every key
        name: web-config
    volumeMounts:
    - name: nginx-conf                # pattern 3: keys become files
      mountPath: /etc/nginx/conf.d
  volumes:
  - name: nginx-conf
    configMap:
      name: web-config
      items:
      - key: nginx.conf
        path: default.conf

The update rule (memorise this). Environment variables are resolved once, when the container starts. Changing the ConfigMap afterwards has no effect until the Pod is recreated. Mounted volumes are different: the kubelet refreshes the projected files periodically, so a config file mounted from a ConfigMap can change under a running process — provided the application actually re-reads it.

Immutable ConfigMaps

Setting immutable: true on a ConfigMap or Secret prevents any further edits. This is not just a safety measure: it lets the kubelet stop watching the object, which measurably reduces API server load in clusters with thousands of Pods. To change an immutable object you create a new one and update the Pod template — which is exactly the immutable-infrastructure pattern.


3. Secrets

A Secret looks almost identical to a ConfigMap but is intended for sensitive values. The differences that matter:

AspectConfigMapSecret
Intended dataNon-confidential settingsPasswords, tokens, keys, certificates
Wire/stored encodingPlain text in database64 in data, or plain text in stringData on write
Encryption at restNoNot by default — requires an EncryptionConfiguration on the API server
kubelet handlingWritten to node diskHeld in tmpfs (memory) when mounted, never written to node disk
Size limit~1 MiB~1 MiB
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: production
type: Opaque
stringData:            # stringData accepts plain text; the API server base64-encodes it
  username: appuser
  password: "S3cur3-P@ss"

Common Secret Types

typePurpose
OpaqueThe default: arbitrary user-defined key-value data
kubernetes.io/dockerconfigjsonPrivate registry credentials, referenced by imagePullSecrets
kubernetes.io/tlsA TLS certificate/key pair, consumed by Ingress spec.tls.secretName
kubernetes.io/service-account-tokenA ServiceAccount API token (largely superseded by projected tokens)
kubernetes.io/basic-auth, kubernetes.io/ssh-authStructured credential formats

base64 Is Not Encryption

This is the single most-tested security point in the topic. echo cGFzc3dvcmQ= | base64 -d reveals the value instantly — base64 is a transport encoding, nothing more. To actually protect Secrets you need a combination of:

  • Encryption at rest: an EncryptionConfiguration on kube-apiserver, ideally backed by an external KMS provider so the key never lives on the control plane disk.
  • RBAC: restrict get, list, and watch on secrets to the workloads and humans that genuinely need them.
  • External secret stores: HashiCorp Vault, cloud secret managers, or the External Secrets Operator / Secrets Store CSI Driver, which sync material in at runtime instead of storing it in etcd at all.
  • automountServiceAccountToken: false for Pods that never call the API server.

4. Choosing an Injection Method

RequirementUse
A handful of scalar settings the app reads at bootEnvironment variables
A whole config file, certificate, or keystoreVolume mount
Values that must change without a redeployVolume mount (plus an app that re-reads)
Registry credentialsimagePullSecrets referencing a dockerconfigjson Secret
Anything larger than about 1 MiBNeither — use a PersistentVolume or an object store

Design caution: injecting a Secret as an environment variable exposes it to anything that can read /proc/<pid>/environ, and it commonly leaks into crash dumps and log lines. Volume mounts are the safer default for genuinely sensitive material.

Test Your Knowledge

An operator edits a ConfigMap that a running Pod consumes as environment variables. What happens to the running container?

A
B
C
D
Test Your Knowledge

What level of protection does a standard Kubernetes Secret provide for its stored values?

A
B
C
D
Test Your Knowledge

Which Secret type is referenced by a Pod's imagePullSecrets field to authenticate against a private container registry?

A
B
C
D