ConfigMaps & Secrets
BeginnerConfigMaps store non-sensitive configuration; Secrets store sensitive data (passwords, tokens, certs). Both decouple configuration from container images and can be injected as environment variables or mounted as files.
Overview
Baking configuration directly into container images means rebuilding images for every environment change. ConfigMaps and Secrets decouple configuration from images, following the 12-factor app methodology. ConfigMaps store non-secret config (database URLs, feature flags, config files). Secrets store sensitive data (database passwords, API keys, TLS certificates) — they are base64-encoded (not encrypted by default; use etcd encryption or external secrets managers for production). Both can be injected as environment variables or mounted as files inside pods.
ConfigMap
ConfigMaps store key-value configuration data. They can be created from YAML, literal values, or config files.
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
data:
LOG_LEVEL: "info"
DATABASE_HOST: "postgres.production.svc.cluster.local"
DATABASE_PORT: "5432"
MAX_CONNECTIONS: "100"
# Multi-line value: a full config file
nginx.conf: |
server {
listen 80;
location /health { return 200 'OK'; }
}
---
# Use ConfigMap in a Deployment — method 1: envFrom (all keys as env vars)
spec:
containers:
- name: app
image: my-app:v1.2.3
envFrom:
- configMapRef:
name: my-app-config
# method 2: specific key as env var
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: my-app-config
key: LOG_LEVEL
# method 3: mount as file
volumeMounts:
- name: config-vol
mountPath: /etc/nginx/conf.d/
volumes:
- name: config-vol
configMap:
name: my-app-config
items:
- key: nginx.conf
path: nginx.conf
# Result: /etc/nginx/conf.d/nginx.conf is the ConfigMap valueSecrets
Secrets are like ConfigMaps but for sensitive data. Values are base64-encoded (not encrypted at rest by default). Mount as files rather than env vars for better security posture.
# Create secret from CLI (base64 encoding is automatic)
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=supersecret
# Create TLS secret from cert files
kubectl create secret tls my-tls-cert \
--cert=tls.crt --key=tls.key
# secret.yaml (values must be base64-encoded manually if writing YAML)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
username: YWRtaW4= # echo -n 'admin' | base64
password: c3VwZXJzZWNyZXQ= # echo -n 'supersecret' | base64
# stringData: (alternative — plain text, K8s base64-encodes it)
stringData:
username: admin
password: supersecret
---
# Use in a Deployment — mount as file (preferred: not in env, not in ps output)
spec:
containers:
- name: app
volumeMounts:
- name: db-creds
mountPath: /run/secrets
readOnly: true
volumes:
- name: db-creds
secret:
secretName: db-credentials
# App reads: fs.readFileSync('/run/secrets/password').toString().trim()
# As env var (less secure but convenient)
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: passwordProduction Secrets Management
Base64 in etcd is not real encryption. Production secrets management uses external stores with the External Secrets Operator or Vault Agent.
# Problem: K8s Secrets are base64 in etcd (anyone with etcd access can read them)
# Solutions:
# Option 1: etcd Encryption at Rest (built-in)
# /etc/kubernetes/encryption-config.yaml on control plane:
# kind: EncryptionConfiguration
# resources:
# - resources: [secrets]
# providers:
# - aescbc: {keys: [{name: key1, secret: <32-byte-base64-key>}]}
# - identity: {}
# Option 2: External Secrets Operator (ESO) — pulls from AWS/GCP/Vault
# Install: helm install eso external-secrets/external-secrets
# ExternalSecret resource pulls secret from AWS Secrets Manager:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-password
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager
kind: ClusterSecretStore
target:
name: db-credentials # creates this K8s Secret
data:
- secretKey: password
remoteRef:
key: /production/db/password # AWS Secrets Manager path
# Option 3: Vault Agent Injector
# Vault injects secrets as files into pod via init container sidecar
# Annotations on pod:
# vault.hashicorp.com/agent-inject: "true"
# vault.hashicorp.com/role: "my-app"
# vault.hashicorp.com/agent-inject-secret-db-password: "secret/data/my-app/db"Key Points to Remember
- 1ConfigMaps for non-sensitive config; Secrets for passwords, keys, and certs.
- 2Both decouple config from images — change config without rebuilding the image.
- 3Prefer mounting secrets as files over env vars — not visible in process list or docker inspect.
- 4Base64 is encoding, not encryption — etcd encryption at rest or external secret managers are needed for true security.
- 5Updates to ConfigMaps/Secrets are not automatically picked up by running pods — you need a rolling restart.
- 6envFrom: configMapRef injects all keys at once; env.valueFrom.configMapKeyRef injects one key.
Interview Questions
Sign in to ask AriaAre Kubernetes Secrets actually secure?
Why should you mount secrets as files rather than environment variables?
Ask Aria about ConfigMaps & Secrets
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.