The Problem
Your application needs database URLs, feature flags, API keys, and TLS certificates. Baking configuration into container images means rebuilding for every environment. Environment variables work but cannot be updated without restarting pods. You need a way to inject configuration that is environment-specific, updateable, and secure for sensitive values.
Kubernetes solves this with ConfigMaps (non-sensitive config) and Secrets (sensitive data).
Creating ConfigMaps
From literal values
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100 \
--from-literal=FEATURE_NEW_UI=true
From files
kubectl create configmap app-config --from-file=config.properties
kubectl create configmap app-config --from-file=app.conf --from-file=logging.conf
kubectl create configmap nginx-config --from-file=nginx.conf=/path/to/custom-nginx.conf
From YAML manifest
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
FEATURE_NEW_UI: "true"
app.conf: |
server.port=8080
server.host=0.0.0.0
database.pool.size=20
cache.ttl=300
Creating Secrets
From literal values
kubectl create secret generic db-credentials \
--from-literal=username=appuser \
--from-literal=password='S3cur3P@ss!' \
--from-literal=host=db.internal.example.com
From files
kubectl create secret tls app-tls --cert=./tls.crt --key=./tls.key
kubectl create secret generic ssh-keys \
--from-file=id_rsa=./deploy_key \
--from-file=known_hosts=./known_hosts
From YAML with stringData
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
stringData:
username: appuser
password: "S3cur3P@ss!"
connection-string: "postgresql://appuser:S3cur3P@ss!@db.internal:5432/production"
Injecting as Environment Variables
All keys from a ConfigMap
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
template:
spec:
containers:
- name: api
image: myapp:latest
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: db-credentials
Specific keys
containers:
- name: api
image: myapp:latest
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: connection-string
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: CACHE_TTL
valueFrom:
configMapKeyRef:
name: app-config
key: CACHE_TTL
optional: true
Mounting as Volumes
Entire ConfigMap as directory
spec:
containers:
- name: api
image: myapp:latest
volumeMounts:
- name: config-volume
mountPath: /etc/app/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
Specific keys as files
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: app.conf
path: application.conf
- key: logging.conf
path: log4j2.xml
Secrets with restricted permissions
volumes:
- name: tls-certs
secret:
secretName: app-tls
defaultMode: 0400
- name: db-creds
secret:
secretName: db-credentials
defaultMode: 0400
SubPath for single-file mounts
containers:
- name: nginx
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: nginx-config
configMap:
name: nginx-config
Note: subPath mounts do NOT receive automatic updates when the ConfigMap changes.
Updating Without Pod Restart
Volume-mounted ConfigMaps auto-update
When you update a ConfigMap, volume-mounted files update automatically within ~60 seconds:
kubectl edit configmap app-config
kubectl exec deploy/api-server -- cat /etc/app/config/LOG_LEVEL
Environment variables do NOT auto-update
Env vars are set at pod creation. You must restart:
kubectl rollout restart deployment/api-server
Hash annotation pattern for auto-rollout
spec:
template:
metadata:
annotations:
checksum/config: "{{ include (print $.Template.BasePath '/configmap.yaml') . | sha256sum }}"
When the ConfigMap changes, the hash annotation changes, triggering a new rollout.
Immutable ConfigMaps and Secrets
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v2
immutable: true
data:
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
To update, create a new ConfigMap with a different name and update your deployment reference.
Encryption at Rest
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
For EKS, GKE, AKS — encryption at rest is handled by the cloud provider using KMS.
Viewing and Debugging
kubectl get configmap app-config -o yaml
kubectl describe configmap app-config
kubectl get secret db-credentials -o yaml
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
kubectl exec deploy/api-server -- env | sort
kubectl exec deploy/api-server -- ls -la /etc/app/config/
kubectl exec deploy/api-server -- cat /etc/app/config/app.conf
Common Mistakes
subPath and expecting auto-updates — SubPath mounts are not updated when the source ConfigMap changes.defaultMode on secret volumes — Default is 0644 (world-readable). Set to 0400 for sensitive files.Quick Reference
| Task | Command/Config | |
|---|---|---|
| Create from literal | <code class="inline-code">kubectl create configmap name --from-literal=key=value</code> | |
| Create from file | <code class="inline-code">kubectl create configmap name --from-file=file.conf</code> | |
| Create secret | <code class="inline-code">kubectl create secret generic name --from-literal=key=value</code> | |
| View ConfigMap | <code class="inline-code">kubectl get configmap name -o yaml</code> | |
| Decode secret | <code class="inline-code">kubectl get secret name -o jsonpath='{.data.key}' \ | base64 -d</code> |
| Inject as env | <code class="inline-code">envFrom: [configMapRef: {name: cm}]</code> | |
| Mount as volume | <code class="inline-code">volumes: [{configMap: {name: cm}}]</code> | |
| Force restart | <code class="inline-code">kubectl rollout restart deployment/name</code> | |
| Immutable config | <code class="inline-code">immutable: true</code> in manifest |
Summary
Use ConfigMaps for non-sensitive configuration and Secrets for credentials and keys. Mount as volumes when you need auto-updating configuration. Use environment variables for simple key-value injection that does not need runtime updates. Always encrypt secrets at rest and never commit Secret manifests to version control.
---
Frequently Asked Questions
What is the difference between ConfigMaps and Secrets in Kubernetes?
ConfigMaps store non-sensitive configuration data as plain text, while Secrets store sensitive data base64-encoded and can be encrypted at rest. Both can be mounted as files or injected as environment variables. Use ConfigMaps for application config and Secrets for passwords, API keys, and certificates. Secrets have additional RBAC controls and can be managed by external secret operators.
How do I update a ConfigMap without restarting pods?
If the ConfigMap is mounted as a volume, Kubernetes automatically updates the mounted files within 1-2 minutes (the kubelet sync period). However, environment variable references are NOT updated without a pod restart. For immediate updates with volume mounts, use a sidecar that watches for changes or use stakater/Reloader to trigger rolling restarts automatically.
How do I create a Secret from a file?
Use kubectl create secret generic my-secret --from-file=./config.json to create a secret from a file, or --from-literal=password=mysecretvalue for individual values. For TLS certificates, use kubectl create secret tls my-tls --cert=cert.pem --key=key.pem. The file name becomes the key in the secret data map.
Why is my Kubernetes Secret not actually secure?
Kubernetes Secrets are only base64-encoded by default, which is not encryption. Anyone with read access to the namespace can decode them. To secure Secrets properly, enable encryption at rest in etcd, use RBAC to restrict access, and consider external secret managers like Vault or AWS Secrets Manager with the External Secrets Operator. Audit secret access through Kubernetes audit logs.
---
Related Resources
- kubectl Cheatsheet — 71 kubectl commands searchable by task
- DevOps Interview Academy — 60 DevOps interview questions including Kubernetes
- Production Troubleshooting Scenarios — 15 production troubleshooting scenarios
- Secrets Management in DevOps — Broader secrets management strategies beyond K8s
- Kubernetes Pod Troubleshooting Guide — Debugging config-related pod failures