Lesson: ConfigMaps & Secrets
What you'll learn
- Why configuration belongs outside your container image.
- How a ConfigMap stores non-secret settings, as env vars or mounted files.
- How a Secret stores sensitive values, and the base64 caveat.
- When to inject config as environment variables vs mounted files.
Skill gained: you can keep settings and credentials out of your image and feed them to Pods cleanly.
The lesson
A good container image is generic: the same myapp:1.0 image should run in dev, staging, and production. The thing that changes between them is configuration — database URLs, feature flags, API keys. So you must keep config out of the image and inject it at run time. Kubernetes gives you two objects for that: ConfigMap (non-secret) and Secret (sensitive).
1. Why config lives outside the image
If you bake the database URL into the image, you need a different image per environment, and changing a setting means a rebuild. Instead, build once and inject config at deploy time:
myapp:1.0 (one image, no config baked in)
|
+-------+--------+
v v
dev ConfigMap prod ConfigMap <- different values, same image
dev Secret prod Secret
This is the "build once, run anywhere" principle. Your image carries code; the cluster carries config.
2. ConfigMap — non-secret settings
A ConfigMap is a set of key/value pairs for non-sensitive configuration.
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
APP_GREETING: "Hello from the lab"
LOG_LEVEL: "info"
app.properties: |
timeout=30
retries=3
kubectl apply -f web-config.yaml
kubectl get configmap web-config -o yaml
3. Two ways to consume it: env vars vs mounted files
As environment variables — good for a handful of simple values your app reads from the environment:
spec:
containers:
- name: web
image: busybox:1.36
command: ["sh", "-c", "echo $APP_GREETING; sleep 3600"]
env:
- name: APP_GREETING
valueFrom:
configMapKeyRef:
name: web-config
key: APP_GREETING
# or pull ALL keys at once:
envFrom:
- configMapRef:
name: web-config
As mounted files — good for whole config files (nginx.conf, app.properties). Each key becomes a file in the mount directory:
spec:
volumes:
- name: config
configMap:
name: web-config
containers:
- name: web
image: nginx:1.27
volumeMounts:
- name: config
mountPath: /etc/web # creates /etc/web/app.properties, /etc/web/LOG_LEVEL, ...
ConfigMap keys -> mounted as files ConfigMap keys -> env vars
/etc/web/app.properties APP_GREETING=Hello...
/etc/web/LOG_LEVEL LOG_LEVEL=info
Rule of thumb: a few scalar settings -> env vars; a whole file the app expects on disk -> mounted volume.
4. Secret — sensitive values
A Secret is shaped like a ConfigMap but is meant for sensitive data: passwords, tokens, TLS keys. Kubernetes treats Secrets a bit more carefully (kept in memory on nodes, not written to disk by kubelet, separate RBAC) and tools avoid printing them.
The easiest and safest way to create one is to let kubectl do the encoding — never hand-edit base64:
kubectl create secret generic db-secret \
--from-literal=DB_USER=appuser \
--from-literal=DB_PASSWORD=<REDACTED>
Consume it just like a ConfigMap, with secretKeyRef (env) or a secret volume (files):
spec:
containers:
- name: web
image: busybox:1.36
command: ["sh", "-c", "echo using $DB_USER; sleep 3600"]
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-secret
key: DB_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSWORD
5. The base64 caveat — Secrets are NOT encrypted
This trips everyone up, so read it twice. When you look at a Secret, its values are base64-encoded, which looks scrambled:
kubectl get secret db-secret -o yaml
# data:
# DB_PASSWORD: c2VjcmV0cGFzcw== <- this is NOT encryption
Base64 is just encoding — anyone can decode it instantly:
echo 'c2VjcmV0cGFzcw==' | base64 -d # prints the plaintext
So a Secret only adds encoding and handling care, not real secrecy by itself. Practical consequences for the internship:
- Never put real secrets in YAML files you commit to Git. Create them with
kubectl create secret(or a proper secrets manager) instead. - Anyone who can read Secrets in a namespace can read your passwords. Treat read access as sensitive.
- In docs and examples, write secret values as
<REDACTED>, never the real thing.
6. Changing config
If you edit a ConfigMap/Secret consumed as env vars, running Pods do not see the change — you must restart them:
kubectl apply -f web-config.yaml
kubectl rollout restart deploy/web # recreate Pods so they pick up new env
Values consumed as mounted files are eventually updated in place (after a short delay), but most apps only read config at startup, so a rollout restart is the reliable way to apply changes either way.
kubectl describe pod <pod> # see which ConfigMaps/Secrets are mounted/injected
kubectl exec <pod> -- env | sort # verify env vars actually landed
Dig deeper
Search terms
kubernetes configmap vs secretkubernetes configmap as environment variablekubernetes mount configmap as filekubernetes secret base64 not encryptedkubectl rollout restart to reload config
Check yourself
- Why should configuration not be baked into the container image?
- Give one case where you'd inject config as env vars and one where you'd mount it as files.
- How should you create a Secret so you don't hand-edit base64?
- Is a Secret's value encrypted? What is base64, and what does that mean for your Git repo?
- After editing a ConfigMap used as env vars, what must you do for running Pods to see the change?
No comments to display
No comments to display