DevOpsKubernetes

Kubernetes ConfigMaps and Secrets: Manage App Configuration Safely

Learn how to use Kubernetes ConfigMaps and Secrets to manage application configuration and sensitive credentials. Covers creating, mounting, and updating config in Pods.

TT
Daniel Brooks
5 min read
Kubernetes ConfigMaps and Secrets: Manage App Configuration Safely

Hardcoding configuration into your Docker image is an anti-pattern — it means building a new image for every environment and baking credentials into version control. Kubernetes solves this with two purpose-built resources: ConfigMaps for non-sensitive configuration and Secrets for sensitive values like passwords and API keys.


ConfigMaps

A ConfigMap stores configuration as key-value pairs. The values can be simple strings or entire file contents. ConfigMaps are stored in etcd in plain text — use Secrets for anything sensitive.

Creating a ConfigMap

From literal values:

bash
kubectl create configmap app-config \
  --from-literal=NODE_ENV=production \
  --from-literal=PORT=3000 \
  --from-literal=LOG_LEVEL=info

From a YAML manifest (recommended):

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  NODE_ENV: "production"
  PORT: "3000"
  LOG_LEVEL: "info"
  DATABASE_HOST: "my-postgres-service"
  DATABASE_PORT: "5432"
bash
kubectl apply -f configmap.yaml
kubectl get configmap app-config
kubectl describe configmap app-config

Using a ConfigMap: Environment Variables

Inject individual keys:

yaml
containers:
  - name: api
    image: myregistry/myapp:1.0
    env:
      - name: NODE_ENV
        valueFrom:
          configMapKeyRef:
            name: app-config
            key: NODE_ENV
      - name: PORT
        valueFrom:
          configMapKeyRef:
            name: app-config
            key: PORT

Or inject all keys at once with envFrom:

yaml
containers:
  - name: api
    image: myregistry/myapp:1.0
    envFrom:
      - configMapRef:
          name: app-config

Every key in app-config becomes an environment variable in the container.

Using a ConfigMap: Mounted as Files

If your app reads configuration from files (nginx.conf, application.yaml, etc.), mount the ConfigMap as a volume:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    server {
      listen 80;
      location / {
        proxy_pass http://api-service:3000;
      }
    }
yaml
containers:
  - name: nginx
    image: nginx:1.25-alpine
    volumeMounts:
      - name: nginx-conf-volume
        mountPath: /etc/nginx/conf.d
        readOnly: true
volumes:
  - name: nginx-conf-volume
    configMap:
      name: nginx-config

The file nginx.conf appears at /etc/nginx/conf.d/nginx.conf inside the container.


Secrets

Secrets are like ConfigMaps, but designed for sensitive data. Values are base64-encoded at rest in etcd (not encrypted by default). Kubernetes also tries to avoid writing Secret values to logs.

Important: base64 encoding is not encryption. Enable Encryption at Rest in production and restrict access with RBAC.

Creating a Secret

From literal values:

bash
kubectl create secret generic db-credentials \
  --from-literal=POSTGRES_USER=myuser \
  --from-literal=POSTGRES_PASSWORD=supersecretpassword \
  --from-literal=POSTGRES_DB=myapp

From a YAML manifest using stringData (Kubernetes base64-encodes it automatically):

yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  POSTGRES_USER: myuser
  POSTGRES_PASSWORD: supersecretpassword
  POSTGRES_DB: myapp

Never commit Secret YAML files with real values to source control. Use tools like Sealed Secrets, External Secrets Operator, or HashiCorp Vault for production.

Using a Secret: Environment Variables

yaml
containers:
  - name: api
    image: myregistry/myapp:1.0
    env:
      - name: DB_PASSWORD
        valueFrom:
          secretKeyRef:
            name: db-credentials
            key: POSTGRES_PASSWORD

Or inject all keys with envFrom:

yaml
containers:
  - name: api
    envFrom:
      - secretRef:
          name: db-credentials

Using a Secret: Mounted as Files

yaml
containers:
  - name: api
    volumeMounts:
      - name: tls-certs
        mountPath: /etc/ssl/certs
        readOnly: true
volumes:
  - name: tls-certs
    secret:
      secretName: my-tls-secret

ConfigMap vs Secret: When to Use Each

Use CaseConfigMapSecret
Database host/port
Feature flags
Log level, environment name
Nginx/app config files
Database passwords
API keys, tokens
TLS certificates
OAuth client secrets

Combining ConfigMap and Secret in a Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-api
  template:
    metadata:
      labels:
        app: my-api
    spec:
      containers:
        - name: api
          image: myregistry/myapp:1.0
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: app-config      # non-sensitive config
            - secretRef:
                name: db-credentials  # sensitive credentials
          resources:
            requests:
              memory: "128Mi"
              cpu: "250m"
            limits:
              memory: "256Mi"
              cpu: "1000m"

Apply all three resources:

bash
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml

Updating ConfigMaps and Secrets

Mounted volumes: Kubernetes updates the files in running Pods within about 60 seconds — no Pod restart needed.

Environment variables: Values are set at container start time. To pick up changes, trigger a rolling restart:

bash
kubectl rollout restart deployment/my-api

This triggers a rolling restart with zero downtime.


Production Best Practices

PracticeDetails
Enable Encryption at RestEncrypt etcd data so Secrets aren't stored in plaintext
Use RBACLimit who can read Secrets in each namespace
Avoid Secrets in YAML filesUse External Secrets Operator, Sealed Secrets, or Vault
Rotate credentialsUpdate the Secret and trigger a rolling restart
Namespace isolationKeep production Secrets in a separate namespace with restricted access

Previous: Lesson 8 — Pods, Deployments & Services | Next: Lesson 10 — Deploy a Docker App to Kubernetes


Part of the Docker & Kubernetes Mastery course.

External references: