Configuration must not be baked into a Docker image. Different environments (dev, staging, production) require different values. K8s solves this with ConfigMap and Secret.
Why Config Must Not Live in the Image
When you hardcode config into an image:
- You have to rebuild the image for every environment
- Secrets end up in the image layer history
- There is no way to update the config without a rebuild
Rule: an image is an immutable artifact. Config and secrets are external dependencies that get mounted at runtime.
ConfigMap: Non-Secret Data
ConfigMap stores non-sensitive settings: service URLs, database names, application parameters.
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
DATABASE_HOST: postgres-service
DATABASE_PORT: "5432"
DATABASE_NAME: myapp
LOG_LEVEL: info
APP_ENV: production
# Apply
kubectl apply -f configmap.yaml
# Create imperatively
kubectl create configmap app-config \
--from-literal=DATABASE_HOST=postgres-service \
--from-literal=LOG_LEVEL=info
# Create from a file
kubectl create configmap nginx-config --from-file=nginx.conf
# Inspect
kubectl get configmap app-config -o yaml
kubectl describe configmap app-config
Secret: Sensitive Data
Secret stores sensitive data: passwords, API keys, tokens. Values are stored as base64 (this is encoding, not encryption — solely for JSON/YAML compatibility).
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
data:
DATABASE_PASSWORD: cGFzc3dvcmQxMjM= # base64("password123")
API_KEY: c2VjcmV0LWFwaS1rZXk= # base64("secret-api-key")
JWT_SECRET: bXlzdXBlcnNlY3JldA== # base64("mysupersecret")
# Encode to base64
echo -n "password123" | base64
# cGFzc3dvcmQxMjM=
# Decode
echo "cGFzc3dvcmQxMjM=" | base64 -d
# password123
# Create a Secret imperatively (safer — does not leave values in shell history)
kubectl create secret generic app-secrets \
--from-literal=DATABASE_PASSWORD=password123 \
--from-literal=API_KEY=secret-api-key
# Inspect (values are redacted)
kubectl get secret app-secrets -o yaml
# data:
# DATABASE_PASSWORD: cGFzc3dvcmQxMjM=
Special Secret types:
# TLS certificate
kubectl create secret tls tls-secret \
--cert=tls.crt \
--key=tls.key
# Docker registry credentials
kubectl create secret docker-registry registry-creds \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=pass
Mounting as Environment Variables
# deployment-with-config.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:1.0
env:
# From ConfigMap — a specific key
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
# From Secret — a specific key
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
# Load all keys from a ConfigMap/Secret at once
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets
Mounting as Files (Volume)
Convenient for configuration files (nginx.conf, .env, application.yaml).
spec:
containers:
- name: app
image: nginx:1.25
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: tls-certs
mountPath: /etc/ssl/certs
readOnly: true
volumes:
- name: nginx-config
configMap:
name: nginx-config-map
- name: tls-certs
secret:
secretName: tls-secret
When mounted into a directory, each key from the ConfigMap/Secret becomes a separate file.
# Inside the container:
ls /etc/nginx/conf.d/
# default.conf upstream.conf
cat /etc/ssl/certs/tls.crt
# -----BEGIN CERTIFICATE-----
# ...
Updating Config Without Restarting
When a ConfigMap or Secret mounted as a volume is changed, the files are updated automatically (with a short delay of ~1 min). Environment variables (env) are not updated — a pod restart is required.
# Edit a ConfigMap
kubectl edit configmap app-config
# Or patch it
kubectl patch configmap app-config \
--patch '{"data":{"LOG_LEVEL":"debug"}}'
# Force-restart pods in a Deployment
kubectl rollout restart deployment/my-app
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!