Mount Configs and Secrets into Kubernetes Pods
How to mount ConfigMaps and Secrets into Kubernetes pods using env vars, volumes, projected volumes, and secret management with external secrets.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Overview
Kubernetes ConfigMaps store non-sensitive configuration data. Secrets store sensitive data like passwords, tokens, and certificates. Both can be mounted into pods as environment variables or files. This approach handles the patterns for injecting configuration and secrets into your applications running in Kubernetes — from basic env vars to projected volumes and external secret operators.
When to Use
- Injecting configuration into containerized applications
- Managing database connection strings, API keys, and certificates
- When different environments (dev, staging, prod) need different configs
- When you need to update config without rebuilding the image
- When secrets must be mounted as files (e.g., TLS certificates)
When NOT to Use
- For non-sensitive config that rarely changes — bake it into the image
- When you need complex config templating — use an init container or Helm
- For secrets in git — use SealedSecrets or External Secrets Operator instead
Solution
Create a ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
LOG_LEVEL: "info"
DATABASE_HOST: "db.internal"
APP_PORT: "8080"
config.yaml: |
server:
port: 8080
timeout: 30s
database:
pool_size: 10
timeout: 5s
Create a Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: default
type: Opaque
stringData:
DATABASE_PASSWORD: "supersecret"
API_KEY: "abc123-def456"
JWT_SECRET: "my-jwt-secret-key"
Mount ConfigMap as environment variables
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
envFrom:
- configMapRef:
name: app-config
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
- name: API_KEY
valueFrom:
secretKeyRef:
name: app-secrets
key: API_KEY
Mount ConfigMap as a volume
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config-volume
mountPath: /etc/app/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
items:
- key: config.yaml
path: config.yaml
Mount Secret as a volume
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: secrets-volume
mountPath: /etc/app/secrets
readOnly: true
volumes:
- name: secrets-volume
secret:
secretName: app-secrets
defaultMode: 0400 # Read-only for owner
Mount TLS certificate as a volume
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-key>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: tls
mountPath: /etc/tls
readOnly: true
volumes:
- name: tls
secret:
secretName: tls-secret
Projected volume (combine ConfigMap + Secret)
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: combined-config
mountPath: /etc/app
readOnly: true
volumes:
- name: combined-config
projected:
sources:
- configMap:
name: app-config
- secret:
name: app-secrets
Selective env vars from ConfigMap
spec:
containers:
- name: app
image: my-app:latest
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
- name: APP_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: APP_PORT
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DATABASE_PASSWORD
Subpath for single file mounting
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config-volume
mountPath: /app/config.yaml
subPath: config.yaml
readOnly: true
volumes:
- name: config-volume
configMap:
name: app-config
Immutable ConfigMaps and Secrets
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
immutable: true
data:
LOG_LEVEL: "info"
APP_PORT: "8080"
External Secrets Operator
# ExternalSecret — syncs secrets from AWS Secrets Manager / Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_PASSWORD
remoteRef:
key: my-app/database
property: password
- secretKey: API_KEY
remoteRef:
key: my-app/api
property: key
Sealed Secrets for gitOps
# SealedSecret — encrypted secret safe to store in git
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: app-secrets
spec:
encryptedData:
DATABASE_PASSWORD: AgB...
API_KEY: AgC...
template:
metadata:
name: app-secrets
type: Opaque
Init container with config rendering
spec:
initContainers:
- name: config-renderer
image: my-config-renderer:latest
env:
- name: APP_ENV
value: "production"
volumeMounts:
- name: config-template
mountPath: /templates
- name: rendered-config
mountPath: /output
command:
- sh
- -c
- |
envsubst < /templates/config.yaml.tpl > /output/config.yaml
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: rendered-config
mountPath: /etc/app/config
readOnly: true
volumes:
- name: config-template
configMap:
name: app-config-template
- name: rendered-config
emptyDir: {}
Variants
Using envFrom with prefix
spec:
containers:
- name: app
image: my-app:latest
envFrom:
- configMapRef:
name: app-config
prefix: APP_
- secretRef:
name: app-secrets
prefix: SECRET_
Hot-reloading ConfigMap in a volume
# ConfigMaps mounted as volumes are auto-updated (typically within 60s)
# Use a file watcher in your app to pick up changes
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config
mountPath: /etc/app/config
readOnly: true
# App should watch for file changes
# e.g., fsnotify in Go, chokidar in Node.js
volumes:
- name: config
configMap:
name: app-config
Docker config secret for image pulls
apiVersion: v1
kind: Secret
metadata:
name: registry-credentials
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <base64-encoded-docker-config>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
imagePullSecrets:
- name: registry-credentials
containers:
- name: app
image: private-registry.io/my-app:latest
Best Practices
-
For a deeper guide, see Kubernetes Config Management Guide.
-
Use ConfigMaps for non-sensitive data, Secrets for sensitive data — never the reverse
-
Set
readOnly: trueon volume mounts — prevents apps from modifying config at runtime -
Use
immutable: truefor stable configs — improves performance and prevents accidental changes -
Use
defaultMode: 0400for secret volumes — restricts file permissions -
Use External Secrets Operator for production — keeps secrets out of etcd and git
-
Use SealedSecrets for GitOps — encrypts secrets at rest in git
-
Don’t store secrets in ConfigMaps — they’re not encrypted at rest in etcd
-
Use
envFromfor bulk loading — cleaner than individualenventries
Common Mistakes
- Storing secrets in ConfigMaps: ConfigMaps are not encrypted. Use Secrets (which are base64-encoded, still not truly encrypted — use ESO or SealedSecrets for real encryption).
- Not setting
readOnly: true: apps can modify mounted config files, causing inconsistent behavior across pod restarts. - Mounting entire ConfigMap when you need one file: use
subPathto mount a single file without overwriting the directory. - Forgetting that Secrets are base64, not encrypted: anyone with
kubectl get secretcan decode them. Use RBAC to restrict access. - Not using
immutable: true: mutable ConfigMaps cause pod restarts on update. Immutable ones are safer and faster.
FAQ
What is the difference between ConfigMap and Secret?
ConfigMaps store non-sensitive data as plaintext. Secrets store sensitive data as base64-encoded strings. Secrets have additional RBAC controls and can be encrypted at rest in etcd.
Are Kubernetes Secrets encrypted?
By default, Secrets are base64-encoded (not encrypted). Enable encryption at rest in etcd with --encryption-provider-config. For true encryption, use External Secrets Operator with AWS KMS or HashiCorp Vault.
How do I update a ConfigMap without restarting pods?
ConfigMaps mounted as volumes are auto-updated (within ~60s). ConfigMaps loaded as env vars require a pod restart. Use a file watcher in your app for hot-reloading volume-mounted configs.
What is a projected volume?
A volume that combines multiple sources (ConfigMaps, Secrets, downward API) into a single directory. Useful for mounting config and secrets together without multiple volume mounts.
How do I create a Secret from a file?
kubectl create secret generic app-secrets \
--from-file=ssl.crt=certs/tls.crt \
--from-file=ssl.key=certs/tls.key \
--from-literal=api-key=abc123 Related Resources
Package Kubernetes Manifests with Helm Charts
How to create, template, and deploy Kubernetes applications using Helm charts, covering values, conditionals, ranges, hooks, subcharts, and library charts.
RecipeOverride Docker Compose Configs per Environment
How to use Docker Compose override files for environment-specific configurations, covering dev, test, staging, production, profiles, and secrets management.
RecipeStore Terraform State in S3 with DynamoDB Locking
How to configure Terraform remote state with S3 backend and DynamoDB locking, covering state isolation, workspace management, encryption, and CI/CD integration.
RecipeSetup SSL Certificates with Let's Encrypt
How to obtain, install, and auto-renew SSL certificates using Certbot with Nginx, Apache, and standalone modes for HTTPS-enabled deployments.