Kubernetes Administration — Level by Level›06 · Configuration & Secrets

Lesson 06 of 32 · Level 1 — Foundations

Configuration & Secrets

Build the image once and configure it everywhere. ConfigMaps hold settings, Secrets hold credentials. Learn both ways to use them, what happens when they change, and why base64 is not encryption.

Beginner → Practitioner
Key wordsConfigMapSecretenv varsvolume mountsbase64encryption at rest

Why configuration lives outside the image

You want one image that runs in dev, staging and production. What changes between them is configuration: database addresses, feature flags, log levels, credentials. Bake those into the image and you need a new build for every environment. Keep them outside, and Kubernetes injects the right values at start-up.

The image is a cake recipe. Every bakery uses the same recipe. But each bakery has its own note on the fridge (ConfigMap) that says "use chocolate icing here" or "make it smaller". The key to the bakery's safe is kept in a locked box (Secret), not written on the fridge where every visitor can read it.

ConfigMaps: plain settings

$ kubectl create configmap app-config \
    --from-literal=APP_COLOR=blue \
    --from-literal=APP_MODE=dev
configmap/app-config created

Or declaratively, which is better for Git:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_COLOR: blue
  APP_MODE: dev
  app.properties: |          # a whole file as one key
    cache.size=256
    feature.newCheckout=true

Secrets: sensitive values

$ kubectl create secret generic db-cred \
    --from-literal=username=app \
    --from-literal=password='S3cr3t!'
secret/db-cred created

Now look at how it is stored:

$ kubectl get secret db-cred -o yaml
apiVersion: v1
kind: Secret
type: Opaque
data:
  password: UzNjcjN0IQ==
  username: YXBw
$ echo 'UzNjcjN0IQ==' | base64 -d
S3cr3t!

base64 is not encryption

Anyone who can get the Secret can read it in one command. Secrets are protected by who is allowed to read them (RBAC), and optionally by encryption at rest in etcd. Never commit Secret YAML to Git. Use a tool such as Sealed Secrets or External Secrets (covered in Kubernetes Security & Hardening).

Two ways to use them

Here is one pod that uses both styles:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 1
  selector:
    matchLabels: { app: app }
  template:
    metadata:
      labels: { app: app }
    spec:
      containers:
        - name: app
          image: busybox:1.36
          command: ["sh", "-c", "echo color=$APP_COLOR user=$DB_USER; cat /etc/app/app.properties; sleep 3600"]
          envFrom:                       # 1) every key → an env var
            - configMapRef:
                name: app-config
          env:                           # 1b) pick a single key from a Secret
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: db-cred
                  key: username
          volumeMounts:                  # 2) keys → files in a folder
            - name: config
              mountPath: /etc/app
              readOnly: true
      volumes:
        - name: config
          configMap:
            name: app-config
$ kubectl apply -f app.yaml
$ kubectl logs deploy/app
color=blue user=app
cache.size=256
feature.newCheckout=true
Environment variables Mounted files
Good for Simple values, 12-factor apps Whole config files (nginx.conf, app.properties)
Updates when the ConfigMap changes? ❌ Only after a pod restart ✅ After a short delay (not with subPath)
Risk Leaks into logs, crash dumps, env output File permissions

Try it: change config and watch what updates

  1. Change the colour: kubectl patch configmap app-config -p '{"data":{"APP_COLOR":"green"}}'
  2. Check the env var: kubectl exec deploy/app -- sh -c 'echo $APP_COLOR' → still blue.
  3. Check the file: kubectl exec deploy/app -- ls -la /etc/app/. The mounted files are symlinks that the kubelet swaps when the ConfigMap changes. After about a minute, cat /etc/app/APP_COLOR shows green.
  4. Now kubectl rollout restart deployment/app and check the env var again → green.

Going deeper: production patterns

  • Roll pods on config change automatically: Helm users add a checksum of the ConfigMap as a pod annotation, so a config change changes the pod template and triggers a rollout.
  • immutable: true on ConfigMaps/Secrets protects against accidental edits and reduces API server load on big clusters. Change config by creating a new named object (for example app-config-v2).
  • Size limit is 1 MiB per ConfigMap/Secret; they're not for large files.
  • Prefer mounting Secrets as files over env vars: env vars are inherited by child processes and show up in crash reports and /proc/<pid>/environ.
  • Turn on encryption at rest (EncryptionConfiguration, ideally with a KMS provider) and audit who can get/list Secrets. list on Secrets is effectively "read all of them".

Recap

  • Keep configuration out of the image: ConfigMaps for settings, Secrets for credentials.
  • Inject as env vars (restart to update) or mounted files (update in place after a delay).
  • Secrets are base64-encoded, not encrypted. Protect them with RBAC, encryption at rest and a proper secret manager.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.