Skip to content

ConfigMaps and Secrets

What You'll Learn

  • How to create a ConfigMap and a Secret, and the two ways to consume either in a Pod
  • Why a Secret being base64-encoded is not the same thing as it being encrypted
  • Where the deeper material on both — and on encryption at rest — actually lives

Why This Matters

Baking configuration and credentials directly into a container image means rebuilding the image every time a value changes, and it means anyone with the image has the credentials. ConfigMaps and Secrets decouple configuration from the image, which is what makes the same image deployable, unmodified, to dev, staging, and prod.

ConfigMaps: Non-Sensitive Configuration

kubectl create configmap app-config \
  --from-literal=DATABASE_URL=postgres://db:5432 \
  --from-literal=LOG_LEVEL=info
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  DATABASE_URL: "postgres://db:5432"
  LOG_LEVEL: "info"
  config.json: |
    {
      "featureFlags": { "newUi": true }
    }

Secrets: Sensitive Values

kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password='change-me'
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: YWRtaW4=       # base64 of "admin"
  password: Y2hhbmdlLW1l   # base64 of "change-me"

base64 is encoding, not encryption

Anyone with get/list permission on Secrets in a namespace — or read access to etcd itself — can trivially recover the plaintext with echo <value> | base64 -d. A Secret's real protection comes from RBAC restricting who can read it and, ideally, encryption at rest for etcd, which is not enabled by default on every distribution. See Secrets and Encryption at Rest for how to actually secure them.

Consuming Either in a Pod

As environment variables:

spec:
  containers:
    - name: app
      image: myapp:1.4.2
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: LOG_LEVEL
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password

As a mounted volume — each key becomes a file, which is preferable for larger values or config files, and (for Secrets) avoids the value showing up in kubectl describe pod output or process-environment dumps:

spec:
  containers:
    - name: app
      image: myapp:1.4.2
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
        - name: secret-volume
          mountPath: /etc/secret
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: app-config
    - name: secret-volume
      secret:
        secretName: db-secret

A mounted ConfigMap or Secret volume updates automatically when the underlying object changes (with a short delay, via the kubelet's sync loop) — env vars set at Pod start do not update until the Pod restarts.

Common Mistakes

  • Treating data in a Secret as encrypted because it looks unreadable — it's just base64, reversible by anyone with the string.
  • Committing a Secret manifest with real values to source control — the YAML data field is plaintext-equivalent, not a safe thing to check in.
  • Expecting an environment variable sourced from a ConfigMap/Secret to update live when the source changes — only volume-mounted values refresh without a Pod restart.
  • Using ConfigMaps for values that are actually sensitive "because it's easier" — if it's a credential, it belongs in a Secret (and ideally an external secret store), not a ConfigMap.

Interview Questions

  • Why is a Kubernetes Secret's base64 encoding not a security control by itself?
  • What's the practical difference between consuming a ConfigMap as an environment variable versus a mounted volume?
  • How would you rotate a database password stored in a Secret without restarting every Pod that consumes it as an env var?

See Interview Prep for full answers.

Next

This page only covers the basics of getting a value into a Pod. For real config-management patterns (immutable ConfigMaps, envFrom, templating with Kustomize/Helm), continue to ConfigMaps In Depth and Secrets In Depth.