ConfigMaps & Secrets
- Configuration Decoupling: Separate settings (ConfigMaps) and sensitive data (Secrets) from application code to maintain portability. Never bake passwords, API keys, or environment-specific configuration into container images.
- Security Awareness: Secrets are only Base64 encoded by default -- this is not encryption. True security requires enabling "Encryption at Rest" in the API server configuration and restricting RBAC access to Secret objects.
- Update Mechanisms: Changes to environment variables require a Pod restart, while changes to volume-mounted configs eventually propagate to the Pod's filesystem (typically within 30-60 seconds via the kubelet sync period).
- Immutability: Mark ConfigMaps and Secrets as
immutable: truein production to prevent accidental changes, improve cluster performance, and force explicit redeployment when configuration changes. - Size Limit: Both ConfigMaps and Secrets are limited to 1 MiB of data. For larger payloads, use external storage or init containers.
Decoupling configuration from application code is a key principle of Cloud Native development. You should never bake passwords or config files into your Docker image. Kubernetes provides two first-class resources for this purpose: ConfigMaps for non-sensitive configuration and Secrets for sensitive data.
1. ConfigMaps
ConfigMap (Yaml)
Pod Container
ConfigMaps allow you to decouple configuration artifacts from image content to keep containerized applications portable. A ConfigMap stores key-value pairs of string data or binary data that can be consumed by Pods as environment variables, command-line arguments, or configuration files mounted as volumes.
Creating ConfigMaps
There are several ways to create a ConfigMap:
From Literal Values
kubectl create configmap app-config \
--from-literal=DATABASE_HOST=postgres.default.svc \
--from-literal=DATABASE_PORT=5432 \
--from-literal=LOG_LEVEL=info
From a File
# Create from a single file
kubectl create configmap nginx-config --from-file=nginx.conf
# Create from a file with a custom key name
kubectl create configmap nginx-config --from-file=main-config=nginx.conf
From an Env File
# app.env contains KEY=VALUE pairs, one per line
kubectl create configmap app-config --from-env-file=app.env
Declarative YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
# Simple key-value pairs
DATABASE_HOST: "postgres.default.svc"
DATABASE_PORT: "5432"
LOG_LEVEL: "info"
# Multi-line configuration file
app.properties: |
server.port=8080
server.context-path=/api
spring.datasource.url=jdbc:postgresql://postgres:5432/mydb
spring.datasource.hikari.maximum-pool-size=10
logging.level.root=INFO
# Another config file
nginx.conf: |
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend:8080;
}
}
Note that all values in a ConfigMap's data field are strings. If you need to store binary data (like a TLS certificate or a compressed file), use the binaryData field, which accepts Base64-encoded values.
Consuming ConfigMaps
As Environment Variables
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:1.0
env:
# Reference individual keys
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DATABASE_PORT
# Or inject ALL keys as env vars at once
envFrom:
- configMapRef:
name: app-config
When using envFrom, each key in the ConfigMap becomes an environment variable name, and the corresponding value becomes the environment variable value. Keys that are not valid environment variable names (e.g., containing dots or hyphens) are skipped.
As Volume Mounts
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:1.0
volumeMounts:
- name: config-volume
mountPath: /etc/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
# Optionally select specific keys
items:
- key: nginx.conf
path: nginx.conf
- key: app.properties
path: application.properties
Each key in the ConfigMap becomes a file inside /etc/config/. The file name is the key, and the file contents are the value. You can use the items field to select specific keys and control the file names.
As Command Arguments
spec:
containers:
- name: app
image: myapp:1.0
command: ["./start.sh"]
args: ["--log-level", "$(LOG_LEVEL)"]
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
2. Secrets
Secrets are used to store small amounts of sensitive data such as passwords, OAuth tokens, SSH keys, and TLS certificates. They are structurally similar to ConfigMaps but have a few important differences:
- Kubernetes can be configured to encrypt Secrets at rest in etcd (ConfigMaps are not encrypted).
- RBAC policies can restrict access to Secrets independently from ConfigMaps.
- Secrets are stored as Base64-encoded data in the
datafield (or plain text in thestringDatafield for convenience during creation).
Base64 Encoding Warning
Base64 is not encryption. It is a reversible encoding scheme that anyone can decode:
# Encoding
echo -n "my-super-secret-password" | base64
# Output: bXktc3VwZXItc2VjcmV0LXBhc3N3b3Jk
# Decoding (trivial)
echo "bXktc3VwZXItc2VjcmV0LXBhc3N3b3Jk" | base64 -d
# Output: my-super-secret-password
Anyone with kubectl get secret -o yaml permissions can read all your Secrets in plain text. Base64 encoding exists only to allow binary data to be stored in YAML/JSON, not for security.
Secret Types
Kubernetes supports several built-in Secret types:
| Type | Description |
|---|---|
Opaque | Default type. Arbitrary user-defined key-value pairs. |
kubernetes.io/dockerconfigjson | Docker registry credentials for pulling private images. |
kubernetes.io/tls | TLS certificate and private key pair. |
kubernetes.io/basic-auth | Basic authentication credentials (username and password). |
kubernetes.io/ssh-auth | SSH private key. |
kubernetes.io/service-account-token | Service account token (auto-created by Kubernetes). |
Creating Secrets
Opaque Secret (YAML)
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
# Use stringData for plain text (Kubernetes encodes to Base64 automatically)
stringData:
username: admin
password: "s3cur3-p@ssw0rd!"
connection-string: "postgresql://admin:s3cur3-p%40ssw0rd!@postgres:5432/mydb"
Using stringData is preferred over data because you provide plain-text values and Kubernetes handles the Base64 encoding. When you retrieve the Secret with kubectl get secret -o yaml, the values appear in the data field as Base64.
Docker Registry Secret
kubectl create secret docker-registry regcred \
--docker-server=https://registry.example.com \
--docker-username=deploy-bot \
--docker-password=ghp_xxxxxxxxxxxx \
--docker-email=deploy@example.com
Then reference it in a Pod or ServiceAccount:
spec:
imagePullSecrets:
- name: regcred
TLS Secret
kubectl create secret tls my-tls-cert \
--cert=path/to/cert.pem \
--key=path/to/key.pem
Consuming Secrets
Secrets are consumed in exactly the same way as ConfigMaps -- as environment variables or volume mounts:
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
containers:
- name: app
image: myapp:1.0
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
volumeMounts:
- name: tls-certs
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls-certs
secret:
secretName: my-tls-cert
defaultMode: 0400 # Restrict file permissions
When mounted as a volume, Secret files are stored in a tmpfs (RAM-backed filesystem) on the node, so they are never written to physical disk.
3. Encryption at Rest
By default, Secrets are stored unencrypted in etcd. Anyone with direct access to etcd can read all Secrets in your cluster. To enable encryption, you must configure an EncryptionConfiguration on the API server:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {} # Fallback to read unencrypted secrets
For production environments, consider using a KMS (Key Management Service) provider instead of static keys. Cloud providers offer KMS integration:
- AWS: AWS KMS with the KMS plugin
- GCP: Cloud KMS (enabled by default on GKE)
- Azure: Azure Key Vault with the KMS plugin
4. Update Behavior: The Critical Difference
Understanding how updates propagate is one of the most important aspects of ConfigMaps and Secrets:
Environment Variables: No Automatic Update
If a ConfigMap or Secret is consumed as an environment variable, changing the ConfigMap or Secret has no effect on running Pods. Environment variables are injected at Pod startup and are never refreshed. You must restart (or recreate) the Pod to pick up changes:
# Restart all Pods in a Deployment to pick up ConfigMap changes
kubectl rollout restart deployment/my-app
Volume Mounts: Eventually Consistent
If a ConfigMap or Secret is mounted as a volume, the kubelet periodically checks for updates and refreshes the mounted files. The update delay depends on:
- The kubelet's
syncFrequency(default: 1 minute) - The ConfigMap cache TTL
In practice, volume-mounted updates propagate within 30 to 90 seconds. Your application must be programmed to watch for file changes (using inotify or periodic file polling) to reload configuration without a Pod restart.
Important caveat: If you use subPath to mount a specific key to a specific file path, that file is never updated. This is a known limitation. Use a full directory mount or a projected volume instead.
5. Immutable ConfigMaps and Secrets
Kubernetes allows you to mark ConfigMaps and Secrets as immutable:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v2
data:
LOG_LEVEL: "warn"
immutable: true
Once set, the immutable field cannot be changed, and the data in the ConfigMap or Secret cannot be modified. You must create a new ConfigMap with a different name and update your Pods to reference it.
Benefits of immutability:
- Protection against accidental changes: Prevents operators from accidentally breaking production by editing a live ConfigMap.
- Performance: The kubelet does not need to watch immutable objects for changes, reducing API server load. In clusters with thousands of ConfigMaps, this makes a measurable difference.
- Auditability: Every configuration change results in a new object, creating a clear audit trail.
6. Real-World Pattern: Versioned Configuration
A common production pattern is to version your ConfigMaps and use Deployments to reference specific versions:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v3
labels:
app: myapp
version: "3"
data:
config.yaml: |
database:
host: postgres.prod.svc
port: 5432
pool_size: 20
cache:
host: redis.prod.svc
ttl: 300
immutable: true
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
metadata:
annotations:
# Forces a rolling update when config changes
configmap-version: "v3"
spec:
containers:
- name: myapp
image: myapp:2.1.0
volumeMounts:
- name: config
mountPath: /etc/app
readOnly: true
volumes:
- name: config
configMap:
name: app-config-v3
When you need to change configuration, you create app-config-v4, update the Deployment to reference it, and Kubernetes performs a rolling update. The old ConfigMap remains available for rollback.
7. Common Pitfalls
-
Treating Base64 as encryption: Base64 is encoding, not encryption. Anyone with read access to the Secret object can decode the values instantly. Always enable encryption at rest and restrict RBAC.
-
Storing Secrets in Git: Never commit Secret YAML files with real credentials to version control. Use sealed-secrets, SOPS, or an external secrets manager (HashiCorp Vault, AWS Secrets Manager) to manage secrets in Git safely.
-
Forgetting the 1 MiB size limit: ConfigMaps and Secrets are limited to 1 MiB. For large configuration files, consider using an init container to download the file or mount a PersistentVolume.
-
Using
subPathand expecting updates: Volume mounts usingsubPathdo not receive automatic updates. If your application depends on live configuration reloading, use a full directory mount. -
Exposing Secrets in logs or environment: Secrets consumed as environment variables can leak into logs, crash dumps, and debugging tools. Prefer volume mounts for sensitive data, and configure your application to read credentials from files rather than environment variables.
-
Not restarting Pods after ConfigMap changes: If you update a ConfigMap consumed as an environment variable, running Pods still have the old values. Use
kubectl rollout restartor tools like Reloader to automate Pod restarts.
8. Best Practices
- Use
stringDatafor Secret creation: It is less error-prone than manually Base64-encoding values in thedatafield. - Mark production ConfigMaps and Secrets as
immutable: true: This prevents accidental modification and reduces kubelet load. - Restrict RBAC access to Secrets: Use Role and RoleBinding to ensure only the Pods and ServiceAccounts that need Secrets can access them. Avoid granting cluster-wide Secret read access.
- Use external secrets managers for production: Tools like HashiCorp Vault, AWS Secrets Manager, or the External Secrets Operator provide audit logging, automatic rotation, and centralized management.
- Use volume mounts for files, env vars for simple values: Mount multi-line configuration files as volumes. Use environment variables for simple key-value settings like feature flags and log levels.
- Add a config hash annotation to Deployments: Include a hash of the ConfigMap contents in a Pod annotation so that changing the ConfigMap triggers a rolling update.
- Rotate Secrets regularly: Implement automated Secret rotation for database passwords, API keys, and TLS certificates. Use short-lived credentials where possible.
What's Next?
- Helm: Learn how to template ConfigMaps and Secrets as part of Helm charts for repeatable deployments.
- Kustomize: Use Kustomize's
configMapGeneratorandsecretGeneratorto automatically generate unique ConfigMap names with content hashes. - Secrets Management: Deep dive into external secrets managers, sealed secrets, and enterprise-grade credential management.
- RBAC: Learn how to restrict who can read and modify Secrets in your cluster.
- Security: Understand the full Kubernetes security model, including network policies, pod security standards, and supply chain security.