What Are ConfigMaps and Secrets?
A core 12-factor principle is to "separate config from code (the image)." Kubernetes implements this with two resources.
- ConfigMap: Stores non-sensitive configuration (app options, URLs, feature flags, and so on) as key-value pairs
- Secret: Stores sensitive data such as passwords, tokens, and certificates (base64 encoding + access control)
Both can be injected into containers as environment variables or volume files, so the same image can be reused across dev/stage/prod with different configuration.
A Secret's default encoding is base64, not encryption. Anyone can decode it, so do not assume "it's a Secret, so it's safe." Real protection comes from RBAC + etcd encryption + external secret-management tools.
Creating a ConfigMap
Imperative (literals / files)
# -
kubectl create configmap app-config \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONN=100
# file (--from-file)
kubectl create configmap nginx-conf --from-file=./nginx.conf
# .env (--from-env-file)
kubectl create configmap app-env --from-env-file=./app.envDeclarative (YAML) — recommended
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
MAX_CONN: "100"
# file settings
application.yaml: |
server:
port: 8080
feature:
newUI: truekubectl apply -f configmap.yaml
kubectl get cm app-config -o yamlCreating a Secret
#
kubectl create secret generic db-secret \
--from-literal=DB_USER=admin \
--from-literal=DB_PASS='S3cr3t!23'
# TLS certificate
kubectl create secret tls web-tls --cert=tls.crt --key=tls.key
#
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=ci --docker-password='token'When writing YAML, values must be base64. To enter plaintext, use stringData.
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData: # → save when base64
DB_USER: admin
DB_PASS: "S3cr3t!23"The main Secret types are as follows.
| type | Purpose |
|---|---|
Opaque | Arbitrary key-value pairs (default) |
kubernetes.io/tls | TLS certificate/key |
kubernetes.io/dockerconfigjson | Registry credentials |
kubernetes.io/service-account-token | SA token |
Injecting into Containers — Environment Variables
Injecting individual keys
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSInjecting everything at once (envFrom)
envFrom:
- configMapRef: { name: app-config }
- secretRef: { name: db-secret }Values injected as environment variables are baked in once when the Pod is created. Updating a ConfigMap does not refresh env vars in already-running Pods. You need a rolling restart for changes to take effect.
Injecting into Containers — Volume Mounts
When you mount a ConfigMap/Secret as files, unlike environment variables, mounted files update automatically when the ConfigMap changes (with a delay of tens of seconds).
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: config-vol
mountPath: /etc/app # /etc/app/application.yaml
readOnly: true
- name: secret-vol
mountPath: /etc/secret
readOnly: true
volumes:
- name: config-vol
configMap:
name: app-config
items: #
- key: application.yaml
path: application.yaml
- name: secret-vol
secret:
secretName: db-secret
defaultMode: 0400 #| Method | Auto-update | Best for |
|---|---|---|
| Environment variables (env/envFrom) | No | Simple values, 12-factor apps |
| Volume mount | Yes (file refresh) | Config files, certificates, hot-reload apps |
Automatic Rolling Restart on Config Changes
Environment-variable injection does not apply automatically, so you must roll the Deployment when config changes. A common pattern is to stamp a hash of the ConfigMap contents into an annotation.
spec:
template:
metadata:
annotations:
# → Pod → when
checksum/config: "a1b2c3..."To roll immediately by hand, use:
kubectl rollout restart deployment/myappCreating an immutable ConfigMap (immutable: true) via kubectl edit or apply prevents accidental changes and reduces API server load. To change it, you must delete and recreate it.
Hardening Security — Actually Protecting Secrets
1) Encryption at rest in etcd
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: ["secrets"]
providers:
- aescbc:
keys:
- name: key1
secret: <base64-32byte-key>
- identity: {}Adding --encryption-provider-config to the API server startup flags stores Secrets encrypted in etcd.
2) Restrict access with RBAC
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: prod
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["db-secret"] # Secret
verbs: ["get"]3) Integrate external secret management
In production, do not store plaintext Secrets in git. Prefer the following:
- External Secrets Operator: Sync from AWS Secrets Manager, Vault, and similar stores
- Sealed Secrets: Commit ciphertext to git; decrypt only inside the cluster
- SOPS: Encrypt files while managing keys with KMS
Verification and Debugging
# Secret check
kubectl get secret db-secret -o jsonpath='{.data.DB_PASS}' | base64 -d; echo
# Pod check
kubectl exec deploy/myapp -- env | grep -E 'LOG_LEVEL|DB_'
# settings file check
kubectl exec deploy/myapp -- cat /etc/app/application.yaml| Symptom | Cause |
|---|---|
| CreateContainerConfigError | Referenced ConfigMap/Secret/key does not exist |
| Empty string value | Typo in key name or case mismatch |
| Files not visible | mountPath overwrites an existing directory (replaced entirely) |
Summary
| Item | ConfigMap | Secret |
|---|---|---|
| Target | Non-sensitive config | Passwords/tokens/certificates |
| Storage | Plaintext | base64 (not encryption) |
| Injection | env / envFrom / volume | Same |
| Auto-update | Volume mount only | Volume mount only |
| Security | RBAC | RBAC + etcd encryption + external tools |
| Applying changes | rollout restart | Same |
Separating config from code is the essence of ConfigMaps and Secrets. Use volume mounts when you need hot reload, environment variables for simple values, and remember that Secrets are only base64—back them with etcd encryption, RBAC, and external secret management for real protection.
AI tools may assist drafting. Check the evidence in each guide for whether commands were run and which environment was used. Confirm the current docs for your version before applying instructions, and email us about errors. email us
Questions & answers
Ask a question about this guide. We'll review and reply.