Skip to content

Kubernetes_configmaps_secrets

Chapter 22: Kubernetes ConfigMaps and Secrets - Configuration Management

Section titled “Chapter 22: Kubernetes ConfigMaps and Secrets - Configuration Management”
  1. Introduction to Configuration Management
  2. What is a ConfigMap?
  3. Creating ConfigMaps
  4. Using ConfigMaps in Pods
  5. What is a Secret?
  6. Creating Secrets
  7. Using Secrets in Pods
  8. Best Practices
  9. Hands-on Lab
  10. Summary

In containerized applications, we need to separate configuration from code. This allows the same container image to run in different environments (dev, staging, production) with different configurations.

┌─────────────────────────────────────────────────────────────────────────────┐
│ CONFIGURATION MANAGEMENT CHALLENGE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Problem: Different environments need different configs │
│ ───────────────────────────────────────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CONTAINER IMAGE │ │
│ │ (Same for all environments) │ │
│ │ │ │
│ │ myapp:v1.0 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ DEV │ │ STAGING │ │ PRODUCTION │ │
│ │ │ │ │ │ │ │
│ │ DB: localhost│ │ DB: staging │ │ DB: prod-db │ │
│ │ DEBUG: true │ │ DEBUG: true │ │ DEBUG: false│ │
│ │ LOG: verbose │ │ LOG: info │ │ LOG: error │ │
│ │ PORT: 3000 │ │ PORT: 8080 │ │ PORT: 80 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Solution: Externalize configuration using ConfigMaps and Secrets │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

A ConfigMap is a Kubernetes resource used to store non-sensitive configuration data in key-value pairs. Pods can consume ConfigMaps as environment variables, command-line arguments, or configuration files in volumes.

┌─────────────────────────────────────────────────────────────────────────────┐
│ CONFIGMAP CONCEPT │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ CONFIGMAP │ │
│ │ metadata: │ │
│ │ name: app-config │ │
│ │ │ │
│ │ data: │ │
│ │ database_url: postgres://db:5432/mydb │ │
│ │ cache_enabled: "true" │ │
│ │ log_level: info │ │
│ │ config.json: | │ │
│ │ { │ │
│ │ "timeout": 30, │ │
│ │ "retries": 3 │ │
│ │ } │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ POD │ │
│ │ │ │
│ │ Environment Variables: │ │
│ │ ├── DATABASE_URL=postgres://db:5432/mydb │ │
│ │ ├── CACHE_ENABLED=true │ │
│ │ └── LOG_LEVEL=info │ │
│ │ │ │
│ │ Files (mounted): │ │
│ │ └── /etc/config/config.json │ │
│ │ { │ │
│ │ "timeout": 30, │ │
│ │ "retries": 3 │ │
│ │ } │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Terminal window
# Create ConfigMap from literal key-value pairs
kubectl create configmap app-config \
--from-literal=database_url=postgres://db:5432/mydb \
--from-literal=cache_enabled=true \
--from-literal=log_level=info
Terminal window
# Create from a file
kubectl create configmap app-config-file \
--from-file=config.json
# Create from multiple files
kubectl create configmap app-multiple-files \
--from-file=app.properties \
--from-file=db.properties
Terminal window
# Create from .env file
kubectl create configmap app-env \
--from-env-file=.env
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
# Simple key-value
database_url: "postgres://db:5432/mydb"
cache_enabled: "true"
log_level: "info"
# Configuration file as value
config.json: |
{
"timeout": 30,
"retries": 3,
"maxConnections": 100
}
# Properties file format
application.properties: |
spring.datasource.url=postgres://db:5432/mydb
spring.datasource.username=admin
server.port=8080
Terminal window
# Apply ConfigMap
kubectl apply -f configmap.yaml

pod-with-configmap-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp
image: myapp:latest
env:
# Simple environment variable
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: app-config
key: database_url
# All configmap values as env vars
envFrom:
- configMapRef:
name: app-config
# With default value
- name: CACHE_ENABLED
valueFrom:
configMapKeyRef:
name: app-config
key: cache_enabled
optional: true # Won't fail if missing
pod-with-configmap-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp
image: myapp:latest
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
# Optional: specific keys
# items:
# - key: config.json
# path: app-config.json
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONFIGMAP USAGE METHODS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Method 1: Environment Variables │
│ ───────────────────────────────────── │
│ pod.spec.containers[].env: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ - name: DATABASE_URL │ │
│ │ valueFrom: │ │
│ │ configMapKeyRef: │ │
│ │ name: app-config │ │
│ │ key: database_url │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ Result: DATABASE_URL=postgres://db:5432/mydb │
│ │
│ Method 2: All env vars at once │
│ ───────────────────────────────────── │
│ pod.spec.containers[].envFrom: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ - configMapRef: │ │
│ │ name: app-config │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ Result: All keys become env vars │
│ │
│ Method 3: Volume Mount │
│ ───────────────────── │
│ pod.spec.volumes[]: │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ - name: config │ │
│ │ configMap: │ │
│ │ name: app-config │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ Result: Files in /etc/config/ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

A Secret is similar to a ConfigMap but is intended to hold small amounts of sensitive data such as passwords, OAuth tokens, and SSH keys. Secrets are stored encoded (base64) by default and can be encrypted at rest.

┌─────────────────────────────────────────────────────────────────────────────┐
│ SECRET CONCEPT │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ SECRET │ │
│ │ metadata: │ │
│ │ name: db-credentials │ │
│ │ │ │
│ │ data: │ │
│ │ username: YWRtaW4= # "admin" base64 encoded │ │
│ │ password: cGFzc3dvcmQ= # "password" base64 encoded │ │
│ │ │ │
│ │ stringData: # Plain text (auto-encoded) │ │
│ │ api-key: my-api-key-12345 │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ POD │ │
│ │ │ │
│ │ Environment Variables (decoded): │ │
│ │ ├── DB_USERNAME=admin │ │
│ │ └── DB_PASSWORD=password │ │
│ │ │ │
│ │ Files (mounted, decoded): │ │
│ │ └── /etc/secret/db-credentials/username │ │
│ │ └── /etc/secret/db-credentials/password │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ Security Note: │
│ • Base64 is NOT encryption - anyone can decode │
│ • Enable encryption at rest for real security │
│ • Use RBAC to restrict Secret access │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

Terminal window
# Create secret from literals
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=secretpassword
Terminal window
# Create secret from file (for certificates, keys)
kubectl create secret tls my-tls-secret \
--cert=path/to/cert.crt \
--key=path/to/key.key
# Create docker registry secret
kubectl create secret docker-registry my-registry-secret \
--docker-username=admin \
--docker-password=secret \
--docker-email=admin@example.com
secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque # Generic secret
data:
# Base64 encoded values
username: YWRtaW4=
password: cGFzc3dvcmQ=
---
apiVersion: v1
kind: Secret
metadata:
name: api-key
type: Opaque
stringData:
# Plain text (will be base64 encoded)
api-key: my-api-key-12345
Terminal window
# Apply secret
kubectl apply -f secret.yaml
┌─────────────────────────────────────────────────────────────────────────────┐
│ SECRET TYPES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Type │ Use Case │
│ ────────────────────┼───────────────────── │
│ Opaque │ Generic key-value pairs (default) │
│ kubernetes.io/tls │ TLS certificates and keys │
│ kubernetes.io/dockerconfigjson │ Docker registry credentials │
│ kubernetes.io/basic-auth │ Basic authentication │
│ kubernetes.io/ssh-auth │ SSH credentials │
│ kubernetes.io/token │ Service account tokens │
│ │
│ Examples: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ # TLS Secret │ │
│ │ apiVersion: v1 │ │
│ │ kind: Secret │ │
│ │ type: kubernetes.io/tls │ │
│ │ metadata: │ │
│ │ name: my-tls │ │
│ │ data: │ │
│ │ tls.crt: <base64 cert> │ │
│ │ tls.key: <base64 key> │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ # Docker Registry Secret │ │
│ │ apiVersion: v1 │ │
│ │ kind: Secret │ │
│ │ type: kubernetes.io/dockerconfigjson │ │
│ │ metadata: │ │
│ │ name: my-registry │ │
│ │ data: │ │
│ │ .dockerconfigjson: <base64 config> │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

pod-with-secret-env.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp
image: myapp:latest
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
# All secret values as env vars
envFrom:
- secretRef:
name: api-key
pod-with-secret-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp
image: myapp:latest
volumeMounts:
- name: secret-volume
mountPath: "/etc/secret"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
# Optional: specific keys
# items:
# - key: username
# path: db-user
pod-with-image-pull-secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp
image: myregistry.io/myapp:latest
imagePullSecrets:
- name: my-registry-secret

┌─────────────────────────────────────────────────────────────────────────────┐
│ BEST PRACTICES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ConfigMaps: │
│ ─────────── │
│ ✓ Use ConfigMaps for non-sensitive configuration │
│ ✓ Version your ConfigMaps │
│ ✓ Use descriptive names │
│ ✓ Group related configs in same ConfigMap │
│ ✓ Use different ConfigMaps per environment │
│ │
│ Secrets: │
│ ──────── │
│ ✓ Enable encryption at rest │
│ ✓ Use RBAC to restrict access │
│ ✓ Rotate secrets regularly │
│ ✓ Don't commit secrets to version control │
│ ✓ Use external secrets management (Vault, AWS Secrets Manager) │
│ │
│ General: │
│ ──────── │
│ ✓ Use the same ConfigMap/Secret across environments │
│ ✓ Don't hardcode values in applications │
│ ✓ Use volume mounts for files, env vars for strings │
│ ✓ Monitor for configuration drift │
│ │
│ Encryption at Rest: │
│ ───────────────── │
│ Enable in kube-apiserver: │
│ --encryption-provider-config=/path/to/encryption.yaml │
│ │
│ encryption.yaml: │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ apiVersion: v1 │ │
│ │ kind: EncryptionConfiguration │ │
│ │ resources: │ │
│ │ - resources: │ │
│ │ - secrets │ │
│ │ providers: │ │
│ │ - aescbc: │ │
│ │ keys: │ │
│ │ - name: key1 │ │
│ │ secret: <base64 32-byte key> │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘

In this hands-on lab, we’ll create and use ConfigMaps and Secrets.

  • A running Kubernetes cluster
Terminal window
# Step 1: Create a ConfigMap from literal values
kubectl create configmap app-config \
--from-literal=database_url=postgres://localhost:5432/mydb \
--from-literal=log_level=info
# Step 2: Create a ConfigMap from a file
cat > config.json << 'EOF'
{
"timeout": 30,
"retries": 3,
"cache": {
"enabled": true,
"ttl": 3600
}
}
EOF
kubectl create configmap app-config-file \
--from-file=config.json
# Step 3: Verify ConfigMaps
kubectl get configmap
kubectl describe configmap app-config
# Step 4: Create a Secret
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=secret123
# Step 5: Verify Secret
kubectl get secret
kubectl describe secret db-credentials
# Decode secret value
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
# Step 6: Create a Pod using ConfigMap and Secret
cat > pod-with-config.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
spec:
containers:
- name: myapp
image: nginx:latest
env:
- name: DATABASE_URL
valueFrom:
configMapKeyRef:
name: app-config
key: database_url
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: app-config-file
EOF
kubectl apply -f pod-with-config.yaml
# Step 7: Verify pod has the values
kubectl exec myapp-pod -- env | grep -E "(DATABASE_URL|DB_)"
kubectl exec myapp-pod -- ls -la /etc/config/
# Step 8: Clean up
kubectl delete pod myapp-pod
kubectl delete configmap app-config app-config-file
kubectl delete secret db-credentials

  1. ConfigMaps - Store non-sensitive configuration data
  2. Secrets - Store sensitive data (base64 encoded)
  3. Usage Methods - Environment variables or volume mounts
  4. Best Practices - Encryption at rest, RBAC, external secrets management
Terminal window
# Create ConfigMap
kubectl create configmap my-config --from-literal=key=value
kubectl create configmap my-config --from-file=config.json
# Create Secret
kubectl create secret generic my-secret --from-literal=key=value
# Use in Pod
# env:
# - name: KEY
# valueFrom:
# configMapKeyRef:
# name: my-config
# key: key
# volumeMounts:
# - name: config
# mountPath: /etc/config
# volumes:
# - name: config
# configMap:
# name: my-config

In the next chapter, we’ll explore Kubernetes Storage (Chapter 23), covering:

  • PersistentVolumes
  • PersistentVolumeClaims
  • StorageClasses