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.
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
- Change the colour:
kubectl patch configmap app-config -p '{"data":{"APP_COLOR":"green"}}' - Check the env var:
kubectl exec deploy/app -- sh -c 'echo $APP_COLOR'→ still blue. - 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_COLORshows green. - Now
kubectl rollout restart deployment/appand 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: trueon ConfigMaps/Secrets protects against accidental edits and reduces API server load on big clusters. Change config by creating a new named object (for exampleapp-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 canget/listSecrets.liston 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.