Configuration Management in Kubernetes
Kubernetes separates configuration from container images so that your applications remain portable across environments. ConfigMaps store non-confidential configuration data, while Secrets store sensitive information like passwords, tokens, and TLS certificates.
ConfigMaps vs Secrets
- ConfigMaps: Non-sensitive key-value pairs, config files, or command-line arguments. Stored in plain text in etcd.
- Secrets: Sensitive data (passwords, API keys, certificates). Base64-encoded in etcd, can be encrypted at rest with KMS.
- Both: Can be consumed as environment variables, command-line arguments, or volume-mounted files.
ConfigMaps
Creating ConfigMaps
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
# Simple key-value pairs
DATABASE_HOST: "postgres-service"
DATABASE_PORT: "5432"
REDIS_URL: "redis://redis-service:6379"
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
# Entire config file as a value
nginx.conf: |
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://api-service:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /health {
return 200 'OK';
add_header Content-Type text/plain;
}
}
app-settings.json: |
{
"feature_flags": {
"new_dashboard": true,
"beta_api": false
},
"pagination": {
"default_page_size": 25,
"max_page_size": 100
}
}
# Create ConfigMap from literal values
kubectl create configmap app-config \
--from-literal=DATABASE_HOST=postgres-service \
--from-literal=DATABASE_PORT=5432 \
--from-literal=LOG_LEVEL=info
# Create ConfigMap from a file
kubectl create configmap nginx-config --from-file=nginx.conf
# Create ConfigMap from a directory (each file becomes a key)
kubectl create configmap app-configs --from-file=./config/
# View ConfigMap
kubectl get configmap app-config -o yaml
kubectl describe configmap app-config
Using ConfigMaps in Pods
# pod-with-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: api-pod
spec:
containers:
- name: api
image: myapp:1.0
# Method 1: Individual environment variables
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_PORT
# Method 2: All keys as environment variables
envFrom:
- configMapRef:
name: app-config
prefix: APP_ # Optional: adds prefix to all keys
# Method 3: Mount as files in a volume
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
readOnly: true
- name: app-settings
mountPath: /app/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: nginx.conf
path: default.conf # Rename the file
- name: app-settings
configMap:
name: app-config
items:
- key: app-settings.json
path: settings.json
Secrets
Creating Secrets
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
# Values must be base64-encoded
username: YWRtaW4= # echo -n 'admin' | base64
password: c3VwZXJTZWNyZXQxMjM= # echo -n 'superSecret123' | base64
connection-string: cG9zdGdyZXM6Ly9hZG1pbjpzdXBlclNlY3JldDEyM0Bwb3N0Z3Jlcy1zZXJ2aWNlOjU0MzIvbXlkYg==
---
# TLS Secret for HTTPS
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTi... # base64-encoded certificate
tls.key: LS0tLS1CRUdJTi... # base64-encoded private key
---
# Docker registry credentials
apiVersion: v1
kind: Secret
metadata:
name: registry-credentials
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: eyJhdXRocyI6ey... # base64-encoded Docker config
# Create a Secret from literal values
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=superSecret123
# Create a TLS Secret
kubectl create secret tls tls-secret \
--cert=path/to/tls.crt \
--key=path/to/tls.key
# Create Docker registry Secret
kubectl create secret docker-registry registry-credentials \
--docker-server=ghcr.io \
--docker-username=myuser \
--docker-password=mytoken
# View Secret (values are base64-encoded)
kubectl get secret db-credentials -o yaml
# Decode a Secret value
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode
Using Secrets in Pods
# pod-with-secrets.yaml
apiVersion: v1
kind: Pod
metadata:
name: api-pod
spec:
containers:
- name: api
image: myapp:1.0
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
volumeMounts:
- name: tls-certs
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: tls-secret
imagePullSecrets:
- name: registry-credentials
External Secret Management
For production environments, Kubernetes Secrets alone may not be sufficient since they are only base64-encoded (not encrypted by default). Use external secret managers like AWS Secrets Manager, HashiCorp Vault, or Google Secret Manager with the External Secrets Operator.
# external-secret.yaml (using External Secrets Operator)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: db-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: production/database
property: username
- secretKey: password
remoteRef:
key: production/database
property: password
Key Takeaways
- ConfigMaps store non-sensitive configuration; Secrets store sensitive data
- Both can be consumed as environment variables or volume-mounted files
- Secrets are base64-encoded, not encrypted — enable encryption at rest in production
- Use External Secrets Operator with cloud secret managers for production-grade secret management
- Never commit Secret manifests with real values to version control