Blog
ConfigMaps and Secrets: managing config in Kubernetes
Hardcoding config values into your container image is a bad idea. ConfigMaps and Secrets are how Kubernetes separates configuration from code.
Article info
Hardcoding config into your container image is a problem. Change a database URL and you have to rebuild the image. Commit a password into a YAML file and it ends up in version control. Kubernetes solves both with two dedicated resource types: ConfigMaps for regular config and Secrets for sensitive data.
ConfigMaps
A ConfigMap stores key-value pairs that your Pods can read at runtime. Environment variables, config files, command-line arguments — anything that changes between environments belongs here, not in the image.
Creating a ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP_ENV: production
LOG_LEVEL: info
MAX_CONNECTIONS: "100" kubectl apply -f configmap.yaml
kubectl get configmap app-config
kubectl describe configmap app-config Using a ConfigMap as environment variables
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: my-app
image: my-app:1.0
envFrom:
- configMapRef:
name: app-config envFrom pulls all keys from the ConfigMap and injects them as environment variables. Your app reads APP_ENV, LOG_LEVEL, and MAX_CONNECTIONS like any other env var.
If you only need specific keys:
env:
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL Using a ConfigMap as a mounted file
Sometimes your app expects a config file, not env vars. You can mount a ConfigMap as a volume:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
server {
listen 80;
location / {
root /usr/share/nginx/html;
}
}
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: config-volume
configMap:
name: nginx-config The file nginx.conf shows up inside the container at /etc/nginx/conf.d/nginx.conf.
Secrets
Secrets work like ConfigMaps but are meant for sensitive data — passwords, API keys, tokens, certificates. Kubernetes stores them base64-encoded and handles them with a bit more care (they’re not shown in logs, can be restricted by RBAC, etc.).
Base64 is not encryption — it’s just encoding. Don’t treat Secrets as fully secure without additional tooling like Sealed Secrets or Vault.
Creating a Secret
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
DB_USER: myuser
DB_PASSWORD: supersecretpassword stringData lets you write plain text — Kubernetes handles the base64 encoding. Apply it the same way:
kubectl apply -f secret.yaml
# List secrets (values are hidden)
kubectl get secret db-credentials
# Decode a value if you need to inspect it
kubectl get secret db-credentials -o jsonpath='{.data.DB_PASSWORD}' | base64 --decode Using a Secret as environment variables
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: my-app
image: my-app:1.0
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD ConfigMap vs Secret — when to use which
| ConfigMap | Secret | |
|---|---|---|
| Database URLs | yes | no |
| Passwords, API keys | no | yes |
| Feature flags | yes | no |
| TLS certificates | no | yes |
| Log levels, timeouts | yes | no |
The rule of thumb: if you’d be uncomfortable committing it to git, use a Secret.
One thing to watch out for
Kubernetes does not automatically restart your Pods when a ConfigMap or Secret changes. If you update a ConfigMap and need the change to take effect, you have to roll the Deployment:
kubectl rollout restart deployment/my-app This creates new Pods with the updated config while gracefully terminating the old ones.
Keeping config out of your image
The point of all this is separation. Your container image should be the same artifact whether it runs in dev, staging, or production. The environment-specific stuff — URLs, feature flags, credentials — lives in ConfigMaps and Secrets, injected at runtime. Change config without rebuilding. Rotate a secret without touching the image.
Next: Namespaces — how to organize all of this when you have more than one team or environment in the same cluster.