13 - ConfigMaps & Secrets
Why ConfigMaps and Secrets?
Separate configuration from code so you can:
- Use the same image across environments (dev/staging/prod)
- Change config without rebuilding images
- Manage sensitive data securely
ConfigMaps
Store non-sensitive configuration data as key-value pairs.
Creating ConfigMaps
bash# From literal values kubectl create configmap app-config \ --from-literal=DATABASE_HOST=db.example.com \ --from-literal=DATABASE_PORT=5432 \ --from-literal=LOG_LEVEL=info # From a file kubectl create configmap nginx-config \ --from-file=nginx.conf # From a directory (each file becomes a key) kubectl create configmap app-configs \ --from-file=./config/ # From .env file kubectl create configmap app-env \ --from-env-file=.env
ConfigMap YAML
yamlapiVersion: v1 kind: ConfigMap metadata: name: app-config namespace: default data: # Simple key-value pairs DATABASE_HOST: "db.example.com" DATABASE_PORT: "5432" LOG_LEVEL: "info" # Multi-line config file app.properties: | server.port=8080 server.host=0.0.0.0 cache.ttl=300 nginx.conf: | server { listen 80; server_name _; location / { proxy_pass http://api:8080; } }
Using ConfigMaps in Pods
yamlapiVersion: v1 kind: Pod metadata: name: my-app spec: containers: - name: app image: myapp:v1 # 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 # All keys become env vars: # DATABASE_HOST=db.example.com # DATABASE_PORT=5432 # LOG_LEVEL=info # Method 3: Mount as files in a volume volumeMounts: - name: config-volume mountPath: /etc/app/config readOnly: true - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf # Mount single file, not directory volumes: - name: config-volume configMap: name: app-config # Each key becomes a file: # /etc/app/config/DATABASE_HOST (content: db.example.com) # /etc/app/config/DATABASE_PORT (content: 5432) - name: nginx-config configMap: name: app-config items: # Mount only specific keys - key: nginx.conf path: nginx.conf
Auto-Updating ConfigMaps
When mounted as a volume, ConfigMap changes are auto-propagated to pods (within ~1 minute). But:
- Environment variables are NOT updated (requires pod restart)
subPathmounts are NOT updated- Apps must watch for file changes to pick up new config
Secrets
Store sensitive data (passwords, tokens, certificates). Base64-encoded (NOT encrypted by default).
Creating Secrets
bash# From literal values kubectl create secret generic db-credentials \ --from-literal=username=admin \ --from-literal=password='S3cr3tP@ss!' # From files kubectl create secret generic tls-cert \ --from-file=tls.crt=./server.crt \ --from-file=tls.key=./server.key # TLS secret (special type) kubectl create secret tls my-tls \ --cert=server.crt \ --key=server.key # Docker registry credentials kubectl create secret docker-registry regcred \ --docker-server=ghcr.io \ --docker-username=myuser \ --docker-password=mytoken
Secret YAML
yamlapiVersion: v1 kind: Secret metadata: name: db-credentials type: Opaque # Generic secret data: # Values MUST be base64 encoded username: YWRtaW4= # echo -n "admin" | base64 password: UzNjcjN0UEBzcyE= # echo -n "S3cr3tP@ss!" | base64 --- # Or use stringData for plain text (auto-encoded) apiVersion: v1 kind: Secret metadata: name: db-credentials type: Opaque stringData: username: admin password: "S3cr3tP@ss!"
Secret Types
| Type | Description |
|---|---|
Opaque | Generic (default) |
kubernetes.io/tls | TLS cert/key pair |
kubernetes.io/dockerconfigjson | Docker registry auth |
kubernetes.io/basic-auth | Username/password |
kubernetes.io/ssh-auth | SSH private key |
kubernetes.io/service-account-token | Service account token |
Using Secrets in Pods
yamlapiVersion: v1 kind: Pod metadata: name: my-app spec: containers: - name: app image: myapp:v1 # As environment variables env: - name: DB_USERNAME valueFrom: secretKeyRef: name: db-credentials key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password # As files volumeMounts: - name: secrets mountPath: /etc/secrets readOnly: true # Image pull secret (private registry) imagePullSecrets: - name: regcred volumes: - name: secrets secret: secretName: db-credentials defaultMode: 0400 # Read-only for owner # Files created: # /etc/secrets/username (content: admin) # /etc/secrets/password (content: S3cr3tP@ss!)
Security Warning: Secrets Are NOT Encrypted
By default, Kubernetes Secrets are:
- Base64-encoded (NOT encryption)
- Stored in plaintext in etcd
- Visible to anyone with API access
Securing Secrets
yaml# 1. Enable encryption at rest in etcd # /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: EncryptionConfiguration resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: <base64-encoded-32-byte-key> - identity: {} # Fallback to plaintext for reading old secrets
bash# 2. Use external secret managers # - AWS Secrets Manager + External Secrets Operator # - HashiCorp Vault + Vault Secrets Operator # - Azure Key Vault + CSI Secret Store Driver # - GCP Secret Manager # 3. RBAC: restrict who can read secrets # See 18 - RBAC & Security # 4. Audit logging: track secret access
External Secrets Operator (Recommended)
yaml# Syncs secrets from external providers to K8s Secrets apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: db-credentials spec: refreshInterval: 1h secretStoreRef: name: aws-secrets-manager kind: ClusterSecretStore target: name: db-credentials data: - secretKey: username remoteRef: key: production/db property: username - secretKey: password remoteRef: key: production/db property: password
Immutable ConfigMaps and Secrets
yamlapiVersion: v1 kind: ConfigMap metadata: name: app-config-v2 immutable: true # Cannot be modified after creation data: LOG_LEVEL: "warn"
Benefits:
- Protects against accidental changes
- Improves cluster performance (no watch needed)
- Must create new version and update pod references to change
kubectl Commands
bash# ConfigMaps kubectl get configmaps kubectl describe configmap app-config kubectl get configmap app-config -o yaml kubectl edit configmap app-config kubectl delete configmap app-config # Secrets kubectl get secrets kubectl describe secret db-credentials # Shows keys but not values kubectl get secret db-credentials -o yaml # Shows base64 values kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d # Decode a secret value echo "UzNjcjN0UEBzcyE=" | base64 -d # S3cr3tP@ss!
FAANG Interview Angle
Common questions:
- "How do you manage configuration in Kubernetes?"
- "Are Kubernetes Secrets secure? How would you improve security?"
- "What's the difference between ConfigMaps and Secrets?"
- "How do you update configuration without restarting pods?"
- "How would you manage secrets across environments?"
Key answers:
- ConfigMaps for non-sensitive config, Secrets for sensitive data; both decouple config from images
- Secrets are base64 (not encrypted); improve with etcd encryption, RBAC, external secret managers
- ConfigMaps: plaintext, no size warnings. Secrets: base64, special handling, 1MB limit
- Volume-mounted ConfigMaps auto-update; env vars require restart
- External Secrets Operator syncing from Vault/AWS Secrets Manager per environment