10.43 - Config and Secrets
Goal of the day: no configuration baked into images. Everything your app needs (env vars, files, credentials) comes from the cluster at runtime.
ConfigMap: non-secret config
ConfigMaps hold strings (or files). Two consumption modes:
- As env vars (snapshot at pod creation; changing the ConfigMap does NOT update running pods).
- As mounted files (live: kubelet updates the file on change; the app must re-read it to notice).
Env vars
apiVersion: v1
kind: ConfigMap
metadata: { name: app-config }
data:
LOG_LEVEL: info
PORT: "8080"
---
# in a pod spec:
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef: { name: app-config, key: LOG_LEVEL }
- name: PORT
valueFrom:
configMapKeyRef: { name: app-config, key: PORT }
Mounted files
# in a pod spec:
volumes:
- name: config
configMap: { name: app-config }
containers:
- volumeMounts:
- { name: config, mountPath: /etc/myapp }
Each key becomes a file: /etc/myapp/LOG_LEVEL, /etc/myapp/PORT.
Perfect for config files (nginx.conf, application.properties) and
dotenv-style loading.
Imperative shortcuts
kubectl create configmap app-config --from-literal=LOG_LEVEL=info
kubectl create configmap nginx-conf --from-file=nginx.conf=./nginx.conf
kubectl create configmap dir-conf --from-file=./conf.d/ # whole dir
kubectl get configmap app-config -o yaml
Secret: config that must not be readable by everyone
Secrets are the same mechanism with one difference: they are meant for
credentials, and (in a real cluster) can be encrypted at rest and gated by
RBAC. IMPORTANT: the data you see in kubectl get secret -o yaml is
base64, which is ENCODING, not ENCRYPTION. Anyone who can read the Secret
can decode it.
kubectl create secret generic dbpass --from-literal=password=supersecret
kubectl get secret dbpass -o yaml
# data:
# password: c3VwZXJzZWNyZXQ= <- base64 of "supersecret"
echo c3VwZXJzZWNyZXQ= | base64 -d # supersecret
In manifests, use stringData so the file stays human-readable; the API
server encodes it to data for you:
apiVersion: v1
kind: Secret
metadata: { name: dbpass }
type: Opaque
stringData:
password: supersecret
Using a Secret
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef: { name: dbpass, key: password }
Or as files (mountPath /etc/db, file password). This is how you hand
config files with embedded credentials to an app.
Secrets in k3s
- Stored in the cluster DB (SQLite by default), NOT encrypted at rest by
default. In etcd-backed clusters you can enable encryption at rest; k3s
supports it via
--secrets-encryption. - Anything a pod can read, a pod can leak: the value ends up in the pod’s
env or filesystem. Logs and
kubectl execoutput can exfiltrate it. Treat Secrets as “protected from casual reading”, not as unbreakable. - Better pattern for truly sensitive things: an external vault (SOPS + age for files, or a secrets operator). Note it for 11-next-steps.
The workflow that avoids the “config in image” trap
- Build the image with NO config baked in (defaults only).
- Create ConfigMap(s) for anything non-secret.
- Create Secret(s) for credentials (via
kubectl create secret, or stringData in a manifest file you keep OUT of git). - Wire both in the Deployment manifest (env + mounts).
- Roll out config changes by re-applying the ConfigMap/Secret and doing a
rollout restart if env-var style:
kubectl rollout restart deployment/app.
Gotchas
- Env-var injection is a SNAPSHOT. Update ConfigMap -> env unchanged until pod recreation. Files via volumeMount DO update live (kubelet syncs every ~1 min by default). Design accordingly.
- ConfigMap keys must be valid env var names when used as env (no dashes, no dots). As files, anything goes.
- A ConfigMap or Secret that does not exist -> pod stuck
CreateContainerConfigError.
kubectl describe podshows the exact missing key. Fix the name/key, not the pod. - Secrets are namespaced. A Secret in
labis NOT visible fromblog. kubectl apply -fon a Secret keeps the raw value in your shell history / file. Use--from-literalsparingly and keep Secret manifests out of version control, or use sealed-secrets/SOPS later.
Challenge wrap-up
C10: env injection. C11: file mount. C12: base64 decoding + the “encoding not encryption” answer. If you can do all three without the cheatsheet, you have day 4 down.