Asterrr's Handbook

ConfigMaps and Secrets

Creating ConfigMaps and Secrets from literals, files and env files, consuming them as environment variables or volumes, Secret types, immutability, and when an update actually reaches a running Pod.

Exam tasks: 2.2 (use ConfigMaps and Secrets to configure applications)

The decision: should this setting reach the container as an environment variable or as a file, and what do you have to do after changing it so the app actually sees the new value?

Env var or volume

Env varsVolume (whole directory)Volume with subPath
Picks up changesNo, until the container restartsYes, eventually (kubelet sync plus cache, often under a minute or two)No
GranularityOne key (valueFrom) or all keys (envFrom)All keys as files, or chosen keys with itemsOne file into an existing directory
Visible inkubectl exec ... -- envls of the mount pathThe single file
Good forFlags, URLs, small valuesConfig files, certificates, anything that should hot-reloadDropping one file next to others in an image directory

Exam signal

"The app must see the new value without restarting" means a volume mount without subPath. If the task only says "make the Pods use the new value", a kubectl rollout restart deploy/... after the edit is the quick, reliable answer for env vars.

Creating them

k create cm vane-settings -n wx --from-literal=LOG_LEVEL=debug --from-literal=REGION=north
k create cm vane-files -n wx --from-file=app.ini            # key = app.ini
k create cm vane-files2 -n wx --from-file=config.ini=app.ini # key renamed to config.ini
k create cm vane-env -n wx --from-env-file=vane.env          # each KEY=value line becomes a key

k create secret generic vane-db -n wx --from-literal=DB_USER=vane --from-literal=DB_PASS='s3cr3t!'
k create secret tls vane-tls -n wx --cert=tls.crt --key=tls.key
k create secret docker-registry vane-pull -n wx \
  --docker-server=registry.wx.example --docker-username=ci --docker-password='p4ss'
  • Quote values with shell metacharacters (!, $) in single quotes, or the shell rewrites them.
  • In YAML, Secret data values must be base64; stringData takes plain text and the API server encodes it. Use echo -n when you encode by hand, or the trailing newline ends up in the password.
  • ConfigMaps and Secrets are namespaced, and the Pod must be in the same namespace to use them.
Secret typeCreated withUsed for
Opaquecreate secret genericArbitrary key/value data
kubernetes.io/tlscreate secret tlstls.crt and tls.key for Ingress, Gateway, webhooks
kubernetes.io/dockerconfigjsoncreate secret docker-registryimagePullSecrets for private registries
kubernetes.io/basic-auth, kubernetes.io/ssh-authYAML with type:Credentials with required keys (username/password, ssh-privatekey)
kubernetes.io/service-account-tokenYAML with an annotation naming the ServiceAccountLong-lived token, only when you explicitly need one
bootstrap.kubernetes.io/tokenkubeadm token createNode join tokens in kube-system

Base64 is not encryption

Anyone who can get a Secret can decode it with base64 -d. Protection comes from RBAC on secrets and from encryption at rest, which you turn on with an EncryptionConfiguration file passed to kube-apiserver via --encryption-provider-config. An answer that "secures" a Secret by base64-encoding it twice changes nothing.

Consuming them

apiVersion: v1
kind: Pod
metadata:
  name: vane
  namespace: wx
spec:
  imagePullSecrets:
  - name: vane-pull
  containers:
  - name: vane
    image: registry.wx.example/vane:3.2
    env:
    - name: DATABASE_USER             # name inside the container
      valueFrom:
        secretKeyRef:
          name: vane-db
          key: DB_USER
    envFrom:
    - configMapRef:
        name: vane-settings           # every key becomes a variable
      prefix: CFG_                    # optional: CFG_LOG_LEVEL, CFG_REGION
    volumeMounts:
    - name: conf
      mountPath: /etc/vane            # directory: refreshes on change
      readOnly: true
    - name: tls
      mountPath: /etc/vane/tls
      readOnly: true
  volumes:
  - name: conf
    configMap:
      name: vane-files
      items:                          # optional: only these keys, renamed
      - key: app.ini
        path: vane.ini
  - name: tls
    secret:
      secretName: vane-tls            # note: secretName, not name
      defaultMode: 0400
  • The volume field is configMap.name but secret.secretName. Mixing them up is the most common YAML error on this topic.
  • A missing ConfigMap or key keeps the Pod in ContainerCreating or CreateContainerConfigError. Add optional: true to the reference if the app can start without it.
  • A volume mount replaces the target directory's contents. Mounting into /etc hides everything else in /etc; use a dedicated directory or subPath.
  • Secret volumes are backed by tmpfs on the node, so they never hit the node's disk.
  • projected volumes merge several ConfigMaps, Secrets, the downward API and ServiceAccount tokens into one directory.

Immutable ConfigMaps and Secrets

apiVersion: v1
kind: ConfigMap
metadata:
  name: vane-settings-v4
data:
  LOG_LEVEL: info
immutable: true
  • Once immutable: true is set, data can't change and the flag can't be removed. To change it, create a new object (often with a version suffix) and point the workload at it, which also triggers a rollout.
  • Immutable objects aren't watched by kubelets, which cuts API server load in clusters with many Pods.
1 MiB
Maximum size of a single ConfigMap or Secret.
Never
How often env vars refresh in a running container.
Never
How often a subPath mount refreshes.
Same namespace
Where a Pod's ConfigMaps and Secrets must live.

Legacy: use projected ServiceAccount tokens from the TokenRequest API instead

Clusters before v1.24 created a long-lived token Secret for every ServiceAccount automatically. Current Kubernetes mounts short-lived, auto-rotated tokens through a projected volume instead. Create a kubernetes.io/service-account-token Secret only when something outside the cluster truly needs a non-expiring token, or use kubectl create token <sa> for a temporary one.

Scenarios

Scenario
A Deployment reads `FEATURE_FLAGS` from ConfigMap `toggles` through `env.valueFrom.configMapKeyRef`. You edit the ConfigMap, wait five minutes, and the running Pods still report the old value. What is the correct way to make them use the new value?
Scenario
A Pod mounts ConfigMap `nginx-extra` at `/etc/nginx/conf.d/extra.conf` using `subPath: extra.conf` so the image's other config files stay visible. After the ConfigMap is updated, the file in the container never changes. Why?
Scenario
A Pod that mounts Secret `api-keys` as a volume is stuck in ContainerCreating, and its events say the Secret was not found. `kubectl get secret api-keys -n default` shows the Secret exists. The Pod runs in namespace `billing`. What fixes it?

Drill

kubectl config use-context drill-w2. In namespace kiosk:

  1. Create ConfigMap kiosk-ui with keys THEME=dark and LANG=fi.
  2. Create Secret kiosk-api with key token set to tk-55aa21.
  3. Create Pod kiosk (image busybox:1.36, command sleep 3600) that gets every key of kiosk-ui as env vars and mounts kiosk-api read-only at /run/kiosk.

Further reading

On this page