ConfigMaps and Secrets: Managing Application Configuration in Kubernetes
Inject configuration data and sensitive information into Kubernetes pods using ConfigMaps and Secrets. Learn about mounting strategies, environment variables, and security best practices.
ConfigMaps and Secrets: Managing Application Configuration in Kubernetes
Applications need configuration. Database URLs, feature flags, API keys, and connection strings change between environments. Hardcoding these values into container images defeats the purpose of containerization. Kubernetes provides two resources for injecting configuration into pods: ConfigMap and Secret.
This post explains how to create these resources, inject them into pods, and use them securely.
For Kubernetes basics, see the Kubernetes fundamentals post. For advanced patterns like external secret operators, read the Advanced Kubernetes post.
When to Use / When Not to Use
Use ConfigMaps when
ConfigMaps work well for non-sensitive configuration that changes between environments: feature flags, environment variables, application settings, and configuration files like nginx.conf or config.yaml.
Share ConfigMaps across pods in the same namespace. The key benefit is keeping configuration out of container images so the same image works in dev, staging, and prod.
Use Secrets when
Secrets are for anything that should not appear in plaintext: database passwords, API keys, OAuth tokens, TLS certificates, SSH keys.
Kubernetes stores Secrets as base64-encoded strings in etcd. Without encryption at rest, these are readable by anyone with etcd access. For sensitive production workloads, use External Secret Operators that sync from Vault, AWS Secrets Manager, or similar.
Skip ConfigMaps for
Sensitive data. It is not encrypted by default. If your ConfigMap name ends in _secret or contains passwords, use a Secret instead.
Skip native Kubernetes Secrets for (without encryption at rest)
Highly regulated environments (PCI-DSS, HIPAA) and multi-tenant clusters where you need strict isolation. Enable encryption at rest before using Secrets for anything genuinely sensitive.
ConfigMap/Secret Injection Flow
flowchart TD
CM[ConfigMap<br/>or Secret] -->|kubectl apply| K8s[Kubernetes API Server]
K8s -->|Store| etcd[etcd datastore]
K8s -->|Watch| Pod[Pod with Container]
Pod -->|Mount as| Volume[/etc/config<br/>or /run/secrets]
Pod -->|Inject as| EnvVar[Environment<br/>Variables]
Volume mounts update automatically when the ConfigMap or Secret changes. Environment variables do not update after pod start. For dynamic configuration updates, use volume mounts with application-side file watching or SIGHUP reload.
ConfigMap Creation and Usage Patterns
ConfigMaps store non-sensitive configuration as key-value pairs or files. You create them from literal values, files, or directories.
Creating from literals
kubectl create configmap app-config \
--from-literal=ENVIRONMENT=production \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
Creating from a file
kubectl create configmap nginx-config \
--from-file=nginx.conf
The filename becomes the configmap key. The file contents become the value.
Creating from a directory
kubectl create configmap html-content \
--from-file=html/
Defining in YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
ENVIRONMENT: "production"
LOG_LEVEL: "info"
DATABASE_HOST: "postgres.database.svc.cluster.local"
DATABASE_PORT: "5432"
Injecting ConfigMap into Pods
You can inject ConfigMap values as environment variables or volume mounts.
Environment variables
apiVersion: v1
kind: Pod
metadata:
name: web-app
namespace: production
spec:
containers:
- name: web-app
image: nginx:1.25
envFrom:
- configMapRef:
name: app-config
With envFrom, all keys from the ConfigMap become environment variables. For selective injection, use env:
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: ENVIRONMENT
valueFrom:
configMapKeyRef:
name: app-config
key: ENVIRONMENT
Volume mounts
Mount ConfigMap values as files:
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- name: web-app
image: nginx:1.25
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
volumes:
- name: config
configMap:
name: nginx-config
items:
- key: nginx.conf
path: default.conf
With this setup, /etc/nginx/conf.d/default.conf contains the contents of the nginx.conf key from the ConfigMap.
Secrets Types and Limitations
Secrets work like ConfigMaps but store sensitive data. Kubernetes stores Secrets as base64-encoded strings, not encrypted. Anyone with API access or etcd access can read Secrets.
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
username: cG9zdGdyZXM=
password: c2VjcmV0cGFzc3dvcmQ=
Generate base64 values:
echo -n "postgres" | base64
echo -n "secretpassword" | base64
Kubernetes Secret types
| Type | Use Case |
|---|---|
| Opaque | Arbitrary key-value pairs |
| kubernetes.io/tls | TLS certificates |
| kubernetes.io/dockerconfigjson | Docker registry credentials |
| kubernetes.io/basic-auth | Basic auth credentials |
TLS Secret
apiVersion: v1
kind: Secret
metadata:
name: tls-cert
namespace: production
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t...
Mounting vs Environment Variables
Both approaches work, but they have different properties:
| Aspect | Environment Variables | Volume Mounts |
|---|---|---|
| Update propagation | Pod must restart | Files update automatically (with sync wave) |
| Visibility | Visible in kubectl exec output | Less exposed |
| Format | Single values | Files |
| Size limit | Not suitable for large configs | Good for config files |
Environment variables work well for simple string values. Volume mounts are better for configuration files, certificates, and other structured data.
For configuration files that change, volume mounts update automatically. Kubernetes syncs the ConfigMap to the volume within a short delay (configurable via ConfigMap data section). Your application needs to watch for file changes or reload on SIGHUP.
External Secret Operators
Kubernetes Secrets have limitations. They are not encrypted by default, they have strict size limits (1MB), and they do not integrate with external secret managers. External Secret Operators bridge this gap.
Tools like External Secrets Operator (ESO), HashiCorp Vault, and AWS Secrets Manager integrate with Kubernetes to sync secrets from external stores.
External Secrets Operator example
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: db-secrets
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: production/database
property: password
This ExternalSecret syncs the password field from the Vault key production/database into a Secret named db-secrets. The Operator handles rotation and updates.
ESO supports AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault, and other providers.
Security Hardening for Secrets
Since Kubernetes does not encrypt Secrets by default, you need additional measures for sensitive data:
Enable encryption at rest
Configure the kube-apiserver to encrypt Secrets before storing them in etcd:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aesgcm:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
Enable this in the kube-apiserver configuration:
--encryption-provider-config=/path/to/encryption-config
Use RBAC to restrict Secret access
Limit who can read Secrets using Role-Based Access Control:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
Enable RBAC authentication
Ensure your kube-apiserver has RBAC enabled:
kubectl get pod kube-apiserver -n kube-system -o jsonpath='{.spec.containers[0].command}' | grep -i rbac
Consider third-party solutions
For production workloads with strict security requirements, use a secrets manager with a Kubernetes operator. Vault, AWS Secrets Manager, and similar tools provide audit logs, secret rotation, and encryption that Kubernetes-native Secrets lack.
Conclusion
ConfigMaps and Secrets let you decouple configuration from container images. ConfigMaps handle non-sensitive configuration like environment settings and config files. Secrets store credentials, tokens, and certificates but do not provide encryption by default.
Mount ConfigMaps as volumes for configuration files and as environment variables for simple values. Use External Secret Operators to integrate with external secret managers for production workloads. Enable encryption at rest for Secrets and restrict access with RBAC.
Getting configuration right makes your applications portable across environments and easier to manage at scale. For advanced Kubernetes patterns including custom controllers and operators, see the Advanced Kubernetes post.
Production Failure Scenarios
Secret Not Found at Pod Startup
If a Secret referenced in a pod does not exist when the pod starts, the pod fails to start with SecretNotFound error.
Symptoms: Pod stuck in Pending or ContainerCreating state, Secrets not found in events.
Diagnosis:
kubectl describe pod <pod-name> -n <namespace>
kubectl get secret <secret-name> -n <namespace>
Mitigation: Create Secrets before applying Pods that reference them. Use depends_on in Helm or kustomize to enforce ordering.
ConfigMap Update Not Propagating to Pods
When a ConfigMap is updated, volume-mounted files reflect the change, but environment variables do not update without pod restart.
Symptoms: Application uses stale configuration even after ConfigMap update.
Mitigation: Use volume mounts for configuration that changes at runtime. Implement SIGHUP handling in your application to reload on file change, or restart pods automatically using a tool like Reloader.
Base64 Encoding Errors in Secrets
Kubernetes Secrets store values as base64 strings. If the base64 encoding is wrong (missing padding, wrong characters), the decoded value is corrupted.
Symptoms: Application fails to connect to database with authentication failed, decoded values are garbled.
Mitigation: Always use echo -n "value" | base64 (note the -n to avoid trailing newline) and echo "b64value" | base64 -d for decoding.
Anti-Patterns
Storing Secrets in ConfigMaps
Never store actual secrets (passwords, API keys) in ConfigMaps. ConfigMaps are not encrypted by default and are visible in YAML exports and etcd dumps. Always use Secret resources for sensitive data.
Not Using binaryData for Non-UTF8 Content
ConfigMaps store data as UTF-8 strings. For binary files (certificates, keys), use the binaryData field instead of data and base64-encode the content.
Copying Entire ConfigMap into Every Environment
Different environments (dev, staging, prod) need different configurations. Copying the same ConfigMap across environments leads to hardcoded production URLs in dev or debug flags in production.
Use Kustomize overlays or Helm values to inject environment-specific configuration.
Quick Recap Checklist
Use this checklist when managing ConfigMaps and Secrets:
- Used ConfigMaps for non-sensitive configuration, Secrets for credentials and keys
- Mounted ConfigMaps as volumes for configuration files and dynamic updates
- Injected ConfigMap values as environment variables only for static configuration
- Enabled encryption at rest for etcd to protect Secret data
- Used RBAC to restrict who can read Secrets
- Used External Secret Operators for production workloads requiring audit trails
- Tested application behavior when ConfigMap or Secret values change at runtime
- Used
binaryDatafield for non-UTF8 secret content - Never committed Secret values to version control
- Used
depends_onor ordering tools to ensure Secrets exist before Pods start
Category
Related Posts
Kustomize: Native Kubernetes Configuration Management
Use Kustomize for declarative Kubernetes configuration management without Helm's templating—overlays, patches, and environment-specific customization.
Container Security: Image Scanning and Vulnerability Management
Implement comprehensive container security: from scanning images for vulnerabilities to runtime security monitoring and secrets protection.
Deployment Strategies: Rolling, Blue-Green, and Canary Releases
Compare and implement deployment strategies—rolling updates, blue-green deployments, and canary releases—to reduce risk and enable safe production releases.