ConfigMaps and Secrets
Decouple configuration and sensitive data from images.
ConfigMaps are for regular configuration, Secrets are for sensitive data (passwords, certificates). Both help keep images clean and make configuration changes safer across environments.
This quick start shows common patterns: environment variables, file mounts, checksums for rolling updates, and basic security practices.
Why ConfigMaps and Secrets
- Keep build artifacts immutable and environment-specific settings outside images.
- Rotate credentials without rebuilding containers.
- Standardize configuration across dev, staging, and production.
Create a ConfigMap and Secret
You can create them from YAML or from literals:
kubectl create configmap app-config --from-literal=LOG_LEVEL=info
kubectl create secret generic db-secret --from-literal=DB_PASSWORD='changeme'
Here is the YAML equivalent:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
---
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
DB_PASSWORD: "changeme"
From files and directories
You can also create ConfigMaps and Secrets from files, which is handy for full config files or certificates:
kubectl create configmap app-config --from-file=app.yaml=./app.yaml
kubectl create secret generic tls-secret --from-file=tls.crt=./tls.crt --from-file=tls.key=./tls.key
This preserves file structure and makes it easy to update and version configs alongside your code.
For a directory:
kubectl create configmap app-config --from-file=./config-dir
Inject as environment variables
Use envFrom to load all keys at once:
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: db-secret
Or map specific keys:
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSWORD
Combining with init containers
If your application needs a config file rendered from templates, use an init container to generate it from env vars, then write it to a shared volume that the main container mounts. This avoids baking secrets into the image while keeping config files on disk.
Resource size planning
Keep ConfigMaps focused and small. If your config grows large, split it into multiple ConfigMaps or store large static files in object storage. This reduces update blast radius and makes rollouts faster.
When possible, avoid putting certificates and keys in the same Secret as app config. Separate resources make it easier to rotate sensitive data without touching unrelated settings.
Keep ownership clear by naming resources after the workload they serve.
Mount as files
Mount ConfigMaps and Secrets into the container filesystem:
volumes:
- name: config
configMap:
name: app-config
- name: secret
secret:
secretName: db-secret
volumeMounts:
- name: config
mountPath: /etc/app/config
readOnly: true
- name: secret
mountPath: /etc/app/secret
readOnly: true
This pattern is safer for large configs or TLS certificates.
Rolling updates on changes
ConfigMaps and Secrets do not automatically restart Pods. A common pattern is to add a checksum annotation to your Deployment so any change triggers a rollout:
metadata:
annotations:
checksum/config: "{{ .Values.configChecksum }}"
If you are not using Helm, you can manually restart:
kubectl rollout restart deployment/my-app
Example: manual hash annotation
If you are not using Helm, you can compute a hash of the ConfigMap and set it on the Deployment:
kubectl get configmap app-config -o json | sha256sum
kubectl patch deployment my-app -p '{"spec":{"template":{"metadata":{"annotations":{"config-hash":"<sha>"}}}}}'
Access control summary
Give service accounts only the permissions required to read the specific ConfigMaps or Secrets they need. This keeps blast radius small if a Pod is compromised.
Avoid giving broad read access to all Secrets in a namespace.
Auditing and compliance
If you operate regulated workloads, log access to Secrets and keep an audit trail for config changes. GitOps workflows help here by turning config changes into reviewed pull requests.
Namespaces and scoping
ConfigMaps and Secrets are namespace-scoped. If you use multiple namespaces, ensure the Pod and the config live in the same namespace. Cross-namespace access is not allowed by default.
Security tips
- Secrets are base64-encoded, not encrypted by default. Enable encryption at rest in the API server if you handle sensitive data.
- Restrict access with RBAC and avoid mounting secrets into Pods that do not need them.
- Consider external secret managers (Vault, cloud KMS) for production.
- Avoid checking secrets into git repositories.
Audit cluster-wide usage periodically so unused Secrets can be removed.
Config updates and application reloads
When you change a ConfigMap that is mounted as files, Kubernetes updates the volume contents, but your application might not reload automatically. Make sure your process watches the files or supports a reload signal. For env vars, Pods must be restarted to pick up changes.
If you deploy via Helm, you can hash the ConfigMap content into a Deployment annotation so a change triggers a rollout.
Example: Application Deployment
Below is a minimal Deployment that uses both env vars and file mounts:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
spec:
replicas: 2
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: app
image: nginx:1.27
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: db-secret
volumeMounts:
- name: config
mountPath: /etc/app/config
readOnly: true
- name: secret
mountPath: /etc/app/secret
readOnly: true
volumes:
- name: config
configMap:
name: app-config
- name: secret
secret:
secretName: db-secret
Size limits and best practices
ConfigMaps are limited in size (about 1MiB). Keep them small, and store large artifacts in object storage or container images. Secrets are also limited in size, so certificates should be compact and rotated periodically.
If you need dynamic configuration, consider a dedicated config service or use GitOps to version changes in a repo.
Debugging
Check what is loaded into your Pod:
kubectl describe pod <pod-name>
Inspect the ConfigMap or Secret:
kubectl get configmap app-config -o yaml
kubectl get secret db-secret -o yaml
You can also exec into the Pod to confirm file mounts:
kubectl exec -it <pod-name> -- ls -l /etc/app/config
kubectl exec -it <pod-name> -- ls -l /etc/app/secret
If env vars look wrong, validate the key names and ensure the Secret is in the same namespace.
If files look correct but the app still fails, confirm it reads the right paths and supports reload.
Common pitfalls
- Large ConfigMaps can hit size limits; keep them under 1MiB.
- Secrets mounted as env vars are only read at Pod start.
- If you run multiple containers, ensure the ConfigMap and Secret names are consistent across all of them.
Avoid storing large binary blobs in ConfigMaps. Use object storage and fetch them at runtime instead.
Treat ConfigMaps as configuration, not data storage.
Rotation and lifecycle
Rotate secrets regularly. If you change a secret used as an environment variable, you must restart Pods for the new value to take effect. If mounted as files, the projected volume updates automatically, but most applications still need a reload signal to pick up the change.
Consider a policy where secrets are versioned and rotated on a schedule, then deprecate old versions after a safe window.
If your app supports hot reload, wire it to watch the mounted secret path and reload without downtime.
Practical notes
- Start with a quick inventory:
kubectl get nodes,kubectl get pods -A, andkubectl get events -A. - Compare desired vs. observed state;
kubectl describeusually explains drift or failed controllers. - Keep names, labels, and selectors consistent so Services and controllers can find Pods.
- Document config defaults and overrides so other operators can change them safely.
Quick checklist
- The resource matches the intent you described in YAML.
- Namespaces, RBAC, and images are correct for the target environment.
- Health checks and logs are in place before promotion.
- Secrets are encrypted at rest and access is audited.