Configuration Management: ConfigMaps & Secrets¶
Duration: 35 minutes (20 minutes theory + 15 minutes lab)
Learning Objectives¶
- Externalize configuration using ConfigMaps
- Store sensitive data securely with Secrets
- Consume configuration in Pods
- Map Docker Compose environment variables to Kubernetes
The Problem: Hardcoded Configuration¶
Don't do this:
spec:
containers:
- name: app
image: myapp:v1
env:
- name: DATABASE_HOST
value: "postgres.example.com" # Hardcoded!
- name: API_KEY
value: "secret123" # Exposed in YAML!
Problems:
- Configuration tied to image
- Secrets visible in version control
- Different values per environment require different images
Solution: ConfigMaps and Secrets
ConfigMaps¶
ConfigMap stores non-sensitive configuration data as key-value pairs.
Creating ConfigMaps¶
From Literal Values¶
kubectl create configmap app-config \
--from-literal=database.host=postgres \
--from-literal=database.port=5432 \
--from-literal=log.level=info
From File¶
# config.properties
database.host=postgres
database.port=5432
log.level=info
kubectl create configmap app-config --from-file=config.properties
Declarative (YAML)¶
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
database.host: "postgres"
database.port: "5432"
log.level: "info"
app.properties: |
feature.flag.enabled=true
max.connections=100
Using ConfigMaps in Pods¶
As Environment Variables¶
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:v1
env:
# Single value
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: database.host
# All keys as environment variables
- envFrom:
- configMapRef:
name: app-config
As Volume Mounts (Files)¶
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:v1
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
volumes:
- name: config
configMap:
name: app-config
Files appear at /etc/config/:
/etc/config/database.host/etc/config/database.port/etc/config/log.level
Secrets¶
Secret stores sensitive data (passwords, tokens, keys).
Note: Secrets are base64 encoded, NOT encrypted by default. Use encryption at rest for production.
Creating Secrets¶
From Literal Values¶
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=secretpass123
From Files¶
echo -n 'admin' > username.txt
echo -n 'secretpass123' > password.txt
kubectl create secret generic db-credentials \
--from-file=username=username.txt \
--from-file=password=password.txt
Declarative (YAML)¶
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4= # base64 encoded "admin"
password: c2VjcmV0cGFzczEyMw== # base64 encoded "secretpass123"
Encoding/Decoding:
# Encode
echo -n 'admin' | base64
# Output: YWRtaW4=
# Decode
echo 'YWRtaW4=' | base64 --decode
# Output: admin
Using stringData (not encoded):
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData: # Plain text, Kubernetes encodes it
username: admin
password: secretpass123
Using Secrets in Pods¶
Secrets as Environment Variables¶
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:v1
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
Secrets as Volume Mounts¶
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:v1
volumeMounts:
- name: credentials
mountPath: /etc/secrets
readOnly: true
volumes:
- name: credentials
secret:
secretName: db-credentials
Files appear at:
/etc/secrets/username/etc/secrets/password
ConfigMap vs Secret¶
| ConfigMap | Secret |
|---|---|
| Non-sensitive data | Sensitive data |
| Plain text in etcd | Base64 encoded (configure encryption) |
| Config files, settings | Passwords, API keys, certificates |
| Visible in kubectl get | Hidden by default in kubectl get |
Secret Types¶
# Generic/Opaque (most common)
kubectl create secret generic my-secret --from-literal=key=value
# Docker registry credentials
kubectl create secret docker-registry regcred \
--docker-server=myregistry.com \
--docker-username=user \
--docker-password=pass
# TLS certificates
kubectl create secret tls tls-secret \
--cert=path/to/cert.crt \
--key=path/to/cert.key
# SSH keys
kubectl create secret generic ssh-key \
--from-file=ssh-privatekey=~/.ssh/id_rsa
Docker Compose Comparison¶
Docker Compose¶
version: '3.8'
services:
app:
image: myapp:v1
environment:
- DATABASE_HOST=postgres
- DATABASE_PORT=5432
env_file:
- .env
secrets:
- db_password
secrets:
db_password:
file: ./db_password.txt
Kubernetes¶
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: "postgres"
DATABASE_PORT: "5432"
---
# Secret
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
db_password: "secretpass123"
---
# Deployment using both
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
replicas: 1
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:v1
envFrom:
- configMapRef:
name: app-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: db_password
Updating Configuration¶
ConfigMaps/Secrets Don't Auto-Reload¶
When you update a ConfigMap or Secret, Pods don't automatically restart.
Options:
- Restart Pods manually:
kubectl rollout restart deployment/myapp - Use volume mounts: Files update automatically (with delay)
- Use tools: Reloader, Stakater, etc.
# Update ConfigMap
kubectl edit configmap app-config
# Restart Deployment to pick up changes
kubectl rollout restart deployment/myapp
Immutable ConfigMaps/Secrets¶
Prevent accidental changes:
Once created, cannot be modified. Must delete and recreate.
Best Practices¶
- Use Secrets for sensitive data - never ConfigMaps
- Don't commit Secrets to Git - use sealed-secrets or external-secrets
- Use volume mounts for large configs - env vars have size limits
- Version your ConfigMaps/Secrets - app-config-v1, app-config-v2
- Limit Secret access with RBAC - not all Pods should access all Secrets
- Enable encryption at rest for Secrets in production
- Use stringData for readability when creating Secrets manually
Examples¶
Check examples/ for sample manifests.
Key takeaways¶
- ConfigMaps for non-sensitive configuration
- Secrets for sensitive data (passwords, keys)
- Multiple ways to consume: environment variables or volume mounts
- Updates don't auto-reload - restart Pods or use volumes
- Never commit Secrets to version control
Check your understanding¶
- When would you use a ConfigMap vs a Secret?
- How do you use a ConfigMap as environment variables in a Pod?
- Are Secrets encrypted by default?
- What happens when you update a ConfigMap used by running Pods?
- How can you make a Secret immutable?
Solution
- ConfigMap: non-sensitive config (settings, feature flags). Secret: sensitive data (passwords, API keys)
- Use
envFromwithconfigMapReforenvwithvalueFrom.configMapKeyRef - No, only base64 encoded. Enable encryption at rest for production
- Running Pods won't pick up changes unless restarted (volumes update with delay)
- Set
immutable: truein the Secret manifest
Hands-on¶
Apply the concepts from this section in the lab exercises.
Next section¶
Once you've reviewed the content and completed the lab, proceed to the next section