ConfigMaps and Secrets¶
Config and code should be separated.
In Kubernetes, ConfigMaps and Secrets are the standard resources for injecting runtime configuration into workloads.
- ConfigMap: non-sensitive configuration.
- Secret: sensitive data such as credentials and keys.
ConfigMap Basics¶
Use ConfigMaps for values such as feature flags, endpoints, and app settings.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: info
API_BASE_URL: https://api.internal.example
Secret Basics¶
Use Secrets for credentials, tokens, and certificate material.
Prefer stringData for authoring convenience; Kubernetes will encode into data.
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
DB_USERNAME: app
DB_PASSWORD: supersecret
Base64 encoding is not encryption. Use encryption at rest for etcd and strict RBAC.
Secret types¶
The type field hints at the intended use:
| Type | Purpose |
|---|---|
Opaque |
Arbitrary data (default) |
kubernetes.io/tls |
TLS certificate and key (tls.crt, tls.key) |
kubernetes.io/dockerconfigjson |
Registry pull credential |
kubernetes.io/service-account-token |
Auto-bound service account token (legacy) |
kubernetes.io/ssh-auth |
SSH private key |
bootstrap.kubernetes.io/token |
Bootstrap token for node join |
Use the correct type so controllers and admission webhooks can handle them appropriately.
Injection Patterns¶
1) Environment variables¶
Or individual keys:
2) Mounted files¶
volumes:
- name: config-vol
configMap:
name: app-config
containers:
- name: app
image: ghcr.io/example/app:1.0.0
volumeMounts:
- name: config-vol
mountPath: /etc/app-config
readOnly: true
Secret mount variant:
Update Behavior¶
What happens after you edit a ConfigMap depends entirely on how it was consumed:
- Environment variables: never update. They are captured at container start. The only way to pick up changes is a restart (
kubectl rollout restart). - Mounted volumes: update eventually. The kubelet refreshes mounted keys on its sync cycle -- typically within a minute or so, not instantly.
subPathmounts: never update. Mounting a single key viasubPathcopies the file once; it is permanently frozen. This exception catches a lot of people.- Even when files refresh, the application must re-read them. Most servers read config once at boot.
Because "edit the ConfigMap and hope" is so unpredictable, the most reliable production pattern is to treat config as immutable: create a new ConfigMap (versioned name or content hash), point the Deployment at it, and let the normal rollout deliver the change with full rollback support. Kustomize's configMapGenerator automates exactly this pattern.
Security Practices¶
- Do not store secrets in Git.
- Restrict Secret access with namespace-scoped RBAC.
- Enable etcd encryption at rest.
- Consider external secret managers (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) with a sync controller like External Secrets Operator or Secrets Store CSI Driver for high-security environments. These keep the secret value out of Kubernetes entirely and inject it at runtime.
- Use immutable ConfigMaps and Secrets where frequent mutation is not required. Immutable objects are more efficient (no watch on etcd) and prevent accidental modification.
Operational Tips¶
kubectl get configmap app-config -o yaml
kubectl get secret db-secret -o yaml
kubectl describe pod <pod-name>
Use kubectl describe pod to verify projected env vars and mounted volumes.
Certification notes¶
- CKAD staples:
kubectl create configmap app-config --from-literal=KEY=value --from-file=config.txtandkubectl create secret generic db-secret --from-literal=password=x. Practice both imperative forms. - Decode a secret quickly:
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d. - Remember which injection methods update live (volumes) and which don't (env vars, subPath).