What is a ConfigMap and Secret in Kubernetes? Explained Simply (2026)
ConfigMaps and Secrets separate configuration from code in Kubernetes. Here's what they are, how they work, and when to use each one — explained simply.
Hardcoding config inside a Docker image is a bad idea — you'd need to rebuild the image just to change a port number. ConfigMaps and Secrets solve this by separating configuration from the container.
The Problem They Solve
# BAD — config baked into image
ENV DATABASE_URL=postgres://prod-db:5432/myapp
ENV API_KEY=sk-1234secretkeyIf you change the DB URL or rotate the API key, you rebuild and redeploy the entire image. ConfigMaps and Secrets let you change config without touching the image.
What is a ConfigMap?
A ConfigMap stores non-sensitive configuration data as key-value pairs. Think of it as a config file that lives in Kubernetes.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
DATABASE_HOST: "postgres-service"
DATABASE_PORT: "5432"
LOG_LEVEL: "info"
APP_ENV: "production"
config.yaml: |
server:
port: 8080
timeout: 30s
database:
pool_size: 10ConfigMaps can store:
- Simple key-value pairs (
LOG_LEVEL: info) - Entire config files (
config.yaml: |...) - Multi-line strings
What NOT to put in ConfigMap: passwords, tokens, API keys — use Secret for those.
What is a Secret?
A Secret stores sensitive data — passwords, API keys, TLS certificates, SSH keys.
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
DB_PASSWORD: cG9zdGdyZXNwYXNzd29yZA== # base64 encoded
API_KEY: c2stMTIzNHNlY3JldGtleQ==Important: Secrets are base64 encoded, NOT encrypted by default. Base64 is not security — it's just encoding. Enable encryption at rest in etcd for real security.
# Encode a value
echo -n "mypassword" | base64
# bXlwYXNzd29yZA==
# Decode a value
echo "bXlwYXNzd29yZA==" | base64 --decode
# mypasswordHow to Create Them
ConfigMap
# From literal values
kubectl create configmap app-config \
--from-literal=DB_HOST=postgres \
--from-literal=LOG_LEVEL=info \
-n production
# From a file
kubectl create configmap nginx-config \
--from-file=nginx.conf \
-n production
# From a directory (creates one key per file)
kubectl create configmap app-configs \
--from-file=configs/Secret
# From literal values (kubectl handles base64 automatically)
kubectl create secret generic app-secrets \
--from-literal=DB_PASSWORD=mypassword \
--from-literal=API_KEY=sk-1234 \
-n production
# From files (e.g., TLS certificates)
kubectl create secret tls my-tls \
--cert=tls.crt \
--key=tls.key
# Docker registry credentials
kubectl create secret docker-registry regcred \
--docker-server=ghcr.io \
--docker-username=myuser \
--docker-password=mytokenHow to Use Them in Pods
Method 1: Environment Variables
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
containers:
- name: app
image: my-app:latest
env:
# Single key from ConfigMap
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
# Single key from Secret
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD
# All keys from ConfigMap as env vars
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secretsMethod 2: Volume Mounts (for config files)
spec:
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: config-volume
mountPath: /etc/config # files appear here
- name: secret-volume
mountPath: /etc/secrets
readOnly: true # always readOnly for secrets
volumes:
- name: config-volume
configMap:
name: app-config
- name: secret-volume
secret:
secretName: app-secrets
defaultMode: 0400 # restrictive permissionsWhen mounted as volumes, each key becomes a file:
/etc/config/DATABASE_HOST → contains "postgres-service"
/etc/config/LOG_LEVEL → contains "info"
/etc/config/config.yaml → contains the full YAML
ConfigMap vs Secret: When to Use Which
| Data | Use |
|---|---|
| Database hostname | ConfigMap |
| Database port | ConfigMap |
| Database password | Secret |
| API endpoint URL | ConfigMap |
| API key / token | Secret |
| Log level | ConfigMap |
| TLS certificate | Secret |
| Feature flags | ConfigMap |
| SSH private key | Secret |
| App config file | ConfigMap |
Rule: If you'd be embarrassed for it to be logged or visible in kubectl describe — use Secret.
Watching for Updates
ConfigMaps and Secrets mounted as volumes update automatically (within ~1 minute) when you change them. Environment variables do NOT update — the pod must restart.
# Update a ConfigMap
kubectl edit configmap app-config -n production
# or
kubectl create configmap app-config --from-literal=LOG_LEVEL=debug \
--dry-run=client -o yaml | kubectl apply -f -
# Force pod restart to pick up env var changes
kubectl rollout restart deployment/my-app -n productionImmutable ConfigMaps and Secrets
For production configs that shouldn't change (set and forget), add immutable: true. This also improves performance — Kubernetes stops watching for changes.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-v2
immutable: true # ← cannot be edited, must delete and recreate
data:
APP_VERSION: "2.1.0"Viewing and Debugging
# List ConfigMaps
kubectl get configmaps -n production
# View contents
kubectl describe configmap app-config -n production
kubectl get configmap app-config -o yaml -n production
# List Secrets (values are hidden)
kubectl get secrets -n production
kubectl describe secret app-secrets -n production
# Decode a secret value
kubectl get secret app-secrets -o jsonpath='{.data.DB_PASSWORD}' | base64 --decode
# Check if pod is using the right ConfigMap
kubectl describe pod <pod-name> -n production | grep -A5 "Environment\|Mounts"Best Practices
✅ Never commit Secrets to Git — use Sealed Secrets, External Secrets Operator, or Vault
✅ Enable etcd encryption at rest — Secrets aren't encrypted by default
✅ Use RBAC to restrict Secret access — not everyone needs to read production secrets
✅ Separate configs per environment — app-config-dev, app-config-prod
✅ Use immutable: true for stable production configs
✅ Mount secrets as volumes instead of env vars — easier to rotate without restart
Summary
| ConfigMap | Secret | |
|---|---|---|
| For | Non-sensitive config | Sensitive data |
| Storage | Plain text | Base64 encoded |
| Encrypted at rest | No (by default) | No (by default, enable separately) |
Visible in describe | Yes | No (values hidden) |
| Use as | Env vars or volume | Env vars or volume |
ConfigMaps and Secrets are the standard Kubernetes way to externalize configuration. Master them and you'll never bake config into your images again.
Learn More
- KodeKloud Kubernetes for Beginners — hands-on ConfigMap and Secret labs
- CKA Exam on Udemy — ConfigMaps and Secrets are tested on the CKA exam
Stay ahead of the curve
Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.
Related Articles
How to Set Up HashiCorp Vault for Secrets Management from Scratch (2026)
HashiCorp Vault is the industry standard for secrets management. This step-by-step guide shows you how to install Vault, configure it, and integrate it with Kubernetes.
What is a Service Mesh? Explained Simply (No Jargon)
Service mesh sounds complicated but the concept is simple. Here's what it actually does, why teams use it, and whether you need one — explained without the buzzwords.
What is mTLS? Mutual TLS Explained Simply (with Kubernetes Examples)
mTLS means both sides of a connection verify each other's identity. It's the backbone of zero-trust networking in Kubernetes service meshes. Here's how it works in plain language.