ConfigMaps

3 minute read

Hardcoding configuration values inside container images is a bad practice. ConfigMaps allow you to decouple configuration from your application code, making your containers portable across different environments.

What is a ConfigMap?

A ConfigMap is a Kubernetes object that stores non-sensitive configuration data as key-value pairs. Pods can consume ConfigMaps as:

  • Environment variables
  • Command-line arguments
  • Configuration files mounted as volumes
┌──────────────────────┐       ┌────────────────────┐
│     ConfigMap         │       │       Pod           │
│                      │       │                    │
│  DB_HOST=postgres    │──────►│  env: DB_HOST      │
│  DB_PORT=5432        │       │  env: DB_PORT      │
│  app.conf=<content>  │──────►│  /etc/config/      │
│                      │       │    app.conf        │
└──────────────────────┘       └────────────────────┘

Creating ConfigMaps

From Literal Values

kubectl create configmap app-config \
  --from-literal=DB_HOST=postgres \
  --from-literal=DB_PORT=5432 \
  --from-literal=LOG_LEVEL=info

From a File

Create app.properties:

database.host=postgres
database.port=5432
database.name=myapp
log.level=info
max.connections=100
kubectl create configmap app-config --from-file=app.properties

From an Env File

Create config.env:

DB_HOST=postgres
DB_PORT=5432
LOG_LEVEL=info
kubectl create configmap app-config --from-env-file=config.env

Declarative YAML

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # Simple key-value pairs
  DB_HOST: "postgres"
  DB_PORT: "5432"
  LOG_LEVEL: "info"

  # Multi-line file content
  app.conf: |
    server {
        listen 80;
        server_name localhost;
        location / {
            proxy_pass http://backend:8080;
        }
    }

  application.yaml: |
    spring:
      datasource:
        url: jdbc:postgresql://postgres:5432/myapp
      jpa:
        hibernate:
          ddl-auto: update
kubectl apply -f configmap.yaml

Consuming ConfigMaps

As Environment Variables (Individual Keys)

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: my-app:1.0
    env:
    - name: DATABASE_HOST
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: DB_HOST
    - name: DATABASE_PORT
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: DB_PORT

As Environment Variables (All Keys at Once)

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: my-app:1.0
    envFrom:
    - configMapRef:
        name: app-config

This injects all key-value pairs from the ConfigMap as environment variables.

As a Mounted Volume (Files)

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: nginx:1.27
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: app-config

Each key in the ConfigMap becomes a file in /etc/config/:

/etc/config/DB_HOST          → contains "postgres"
/etc/config/DB_PORT          → contains "5432"
/etc/config/app.conf         → contains the nginx config
/etc/config/application.yaml → contains the Spring config

Mount Specific Keys Only

volumes:
- name: config-volume
  configMap:
    name: app-config
    items:
    - key: app.conf
      path: nginx.conf     # Rename the file

This mounts only app.conf as /etc/config/nginx.conf.

Hands-On: Complete Example

Let’s put it all together:

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  default.conf: |
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html;
        }

        location /health {
            return 200 'healthy\n';
            add_header Content-Type text/plain;
        }
    }
  index.html: |
    <!DOCTYPE html>
    <html>
    <body>
      <h1>Hello from ConfigMap!</h1>
      <p>This page was loaded from a ConfigMap.</p>
    </body>
    </html>
---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-custom
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-custom
  template:
    metadata:
      labels:
        app: nginx-custom
    spec:
      containers:
      - name: nginx
        image: nginx:1.27
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-conf
          mountPath: /etc/nginx/conf.d
        - name: html-content
          mountPath: /usr/share/nginx/html
      volumes:
      - name: nginx-conf
        configMap:
          name: nginx-config
          items:
          - key: default.conf
            path: default.conf
      - name: html-content
        configMap:
          name: nginx-config
          items:
          - key: index.html
            path: index.html
# Apply everything
kubectl apply -f configmap.yaml

# Port-forward and test
kubectl port-forward deployment/nginx-custom 8080:80
# Visit http://localhost:8080 → "Hello from ConfigMap!"
# Visit http://localhost:8080/health → "healthy"

Updating ConfigMaps

Volume-Mounted ConfigMaps Auto-Update

When you update a ConfigMap, volume-mounted files are updated automatically (within ~1 minute). The kubelet periodically syncs mounted ConfigMaps.

# Update the ConfigMap
kubectl edit configmap nginx-config

# Or re-apply with changes
kubectl apply -f configmap.yaml

Note: Environment variables from ConfigMaps do NOT auto-update. The Pod must be restarted to pick up changes.

Force a Pod Restart After ConfigMap Update

# Restart all Pods in a Deployment
kubectl rollout restart deployment/nginx-custom

Inspecting ConfigMaps

# List ConfigMaps
kubectl get configmaps
# or: kubectl get cm

# View contents
kubectl describe configmap app-config

# Output as YAML
kubectl get configmap app-config -o yaml

Immutable ConfigMaps

For ConfigMaps that should never change (reduces load on the API server):

apiVersion: v1
kind: ConfigMap
metadata:
  name: static-config
data:
  VERSION: "1.0.0"
immutable: true

Once set to immutable, the ConfigMap cannot be modified — only deleted and recreated.

Best Practices

Practice Reason
Use ConfigMaps for non-sensitive data only Secrets are for passwords, tokens, keys
Name ConfigMaps descriptively app-config, nginx-conf, not cm1
Version ConfigMaps for rollbacks app-config-v1, app-config-v2
Use volume mounts for config files More flexible, supports hot-reload
Set immutable: true for static configs Reduces API server load

Summary

  • ConfigMaps store non-sensitive configuration as key-value pairs
  • Pods consume them as environment variables or mounted files
  • Volume-mounted ConfigMaps auto-update (~1 minute delay)
  • Environment variable ConfigMaps require a Pod restart to update
  • Use Secrets (next chapter) for sensitive data like passwords and tokens

What’s Next?

ConfigMaps are great for general configuration, but what about passwords, API keys, and certificates? In the next chapter, we’ll learn about Secrets — Kubernetes’ way of handling sensitive data.

Next: Secrets →

Updated: