TL;DR (Quick Summary)#
- ConfigMaps: Store plain-text key-value pairs or whole configuration files (
app.conf). - Secrets: Store base64-encoded sensitive values. Types include
Opaque(generic),kubernetes.io/dockerconfigjson(image pull secrets), andkubernetes.io/tls. - Injection Methods:
- Environment Variables (
env/envFrom). - Volume Mounts (mounted as read-only files inside container filesystems).
- Environment Variables (
- Security Caution: Base64 encoding is NOT encryption. Use RBAC restrictions, etcd encryption-at-rest, or HashiCorp Vault / External Secrets Operator in production.
1. ConfigMaps Deep Dive#
graph LR
CM["ConfigMap: app-config
LOG_LEVEL=debug
DB_HOST=postgres.default"] -->|1. Inject as Env Vars| Pod1["Pod A (env: DB_HOST)"]
CM -->|2. Mount as Volume /etc/config| Pod2["Pod B (File: /etc/config/app.json)"]
Creating a ConfigMap (Declarative)#
Create configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
LOG_LEVEL: "info"
APP_FEATURE_FLAG: "true"
nginx.conf: |
server {
listen 80;
location / {
return 200 "Configured via ConfigMap Volume!";
}
}Apply manifest:
kubectl apply -f configmap.yaml2. Secrets Deep Dive#
Creating a Secret (Imperative & Base64 Encoding)#
Generate base64 strings:
echo -n "super-secret-password" | base64
# Output: c3VwZXItc2VjcmV0LXBhc3N3b3JkCreate secret.yaml:
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: default
type: Opaque
data:
DB_USER: cG9zdGdyZXM= # base64 for 'postgres'
DB_PASSWORD: c3VwZXItc2VjcmV0LXBhc3N3b3Jk # base64 for 'super-secret-password'Apply manifest:
kubectl apply -f secret.yaml3. Consuming ConfigMaps & Secrets in Pods#
Method A: Ingesting Key-Value Pairs as Environment Variables#
Create pod-env.yaml:
apiVersion: v1
kind: Pod
metadata:
name: app-env-pod
spec:
containers:
- name: app-container
image: alpine
command: ["sh", "-c", "env && sleep 3600"]
env:
# Single field from ConfigMap
- name: APPLICATION_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
# Single field from Secret
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORDApply and verify injected environment variables:
kubectl apply -f pod-env.yaml
kubectl exec app-env-pod -- env | grep -E "LOG_LEVEL|DATABASE_PASSWORD"Expected Terminal Output:
APPLICATION_LOG_LEVEL=info
DATABASE_PASSWORD=super-secret-passwordMethod B: Mounting Configuration Files as Volumes#
When mounting a ConfigMap as a Volume, files inside the mounted directory automatically update when the ConfigMap is modified—without requiring a container restart!
Create pod-volume-config.yaml:
apiVersion: v1
kind: Pod
metadata:
name: nginx-configmap-volume-pod
spec:
containers:
- name: web-server
image: nginx:alpine
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf
volumes:
- name: config-volume
configMap:
name: app-config4. Production Security Best Practices#
By default, etcd stores Secrets as plain base64 strings. Follow these production rules:
- Enable Encryption at Rest: Configure
kube-apiserverwith--encryption-provider-configto encryptetcdsecrets using AES-CBC or KMS. - Restrict RBAC Access: Restrict
get,list, andwatchpermissions on Secret resources to authorized service accounts only. - Use External Secrets Operator (ESO): Synchronize secrets dynamically from enterprise secret managers (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault) directly into K8s Secrets.
5. Summary & Next Steps#
ConfigMaps and Secrets handle runtime configuration. However, container filesystems are ephemeral—when a Pod restarts, all files created inside the container are wiped out.
In Episode 08: Persistent Volumes, PVCs & StorageClasses, we will master stateful storage abstractions to persist data across Pod restarts and node failures!

