KubernetesConfigMapKubectlDevOpsConfiguration Management Beginner 12 min read

ConfigMap

Learn what Kubernetes ConfigMap is, why it is used, how it works, where it is used, and how to troubleshoot ConfigMap-related pod issues with real-time examples.

Table of Contents


What is a ConfigMap?

A ConfigMap is a Kubernetes API object used to store non-confidential configuration data as key-value pairs. It decouples environment-specific configuration from your container images, making applications more portable and easier to manage across different environments (development, staging, production).

Think of a ConfigMap as a configuration file stored inside Kubernetes — instead of baking configuration values (like database hostnames, feature flags, log levels, or API endpoints) directly into your Docker image or hardcoding them in your app code, you store them in a ConfigMap and inject them into your pods at runtime.

Note: ConfigMaps are not designed to store sensitive data such as passwords, tokens, or keys. For sensitive data, use Kubernetes Secrets.


Why is ConfigMap Used?

Before ConfigMaps existed, developers faced a common problem: the same Docker image had to be rebuilt every time a configuration value changed for a different environment. This violated the “build once, deploy anywhere” principle.

ConfigMaps solve this by enabling:

1. Environment Separation The same container image can run in dev, staging, and prod with different configurations injected at deploy time — no image rebuilds needed.

2. Cleaner Container Images Your Dockerfile stays lean. Configuration is managed externally by the Kubernetes platform team, not embedded in images.

3. Dynamic Configuration Updates When a ConfigMap is mounted as a volume, Kubernetes can propagate updates to running pods without requiring a pod restart (with some caveats — see below).

4. Separation of Concerns Developers own the application code; platform/ops teams own environment-specific configuration. ConfigMaps enforce this separation at the infrastructure level.

5. GitOps-Friendly ConfigMap YAML files can be committed to Git, reviewed in pull requests, and deployed via CI/CD pipelines alongside your application manifests.


How ConfigMap Works

At a high level, ConfigMap works in three steps:

1. You CREATE a ConfigMap (YAML manifest or kubectl command)
         ↓
2. You REFERENCE the ConfigMap in your Pod spec
         ↓
3. Kubernetes INJECTS the data into the Pod as environment variables or mounted files

Internal Architecture

Kubernetes stores ConfigMap data in etcd (the cluster’s distributed key-value store). When a pod is scheduled and created, the kubelet on the worker node reads the pod spec, identifies any referenced ConfigMaps, fetches the data, and either:

  • Sets environment variables in the container process, or
  • Writes the data to files in a mounted volume inside the container.

ConfigMap Data Limits

  • Maximum size of a ConfigMap is 1 MiB (1,048,576 bytes).
  • For larger configuration files, consider mounting a PersistentVolume or using an external config server (like Vault or AWS AppConfig).

Where ConfigMap is Used

ConfigMaps are used across virtually every Kubernetes workload. Common real-world use cases include:

Use CaseExample
Application settingsLOG_LEVEL=debug, MAX_RETRIES=3
Database connection configDB_HOST=postgres-service, DB_PORT=5432
Feature flagsFEATURE_DARK_MODE=true
External API endpointsPAYMENT_API_URL=https://api.stripe.com
Nginx/Apache config filesFull nginx.conf mounted as a volume
Spring Boot application.propertiesEntire properties file injected into a Java app
Shell scriptsStartup scripts injected into init containers
Prometheus scrape configPrometheus configuration file

ConfigMap Structure and Syntax

Basic Structure

apiVersion: v1
kind: ConfigMap
metadata:
  name: my-app-config
  namespace: default
  labels:
    app: my-app
    env: production
data:
  # Simple key-value pairs
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  APP_PORT: "8080"
  DATABASE_HOST: "postgres-service.default.svc.cluster.local"
  DATABASE_PORT: "5432"

  # Multi-line value (e.g., a config file)
  app.properties: |
    server.port=8080
    spring.datasource.url=jdbc:postgresql://postgres:5432/mydb
    spring.datasource.username=appuser
    logging.level.root=INFO

  # Nginx config as a value
  nginx.conf: |
    server {
      listen 80;
      server_name example.com;
      location / {
        proxy_pass http://backend-service:3000;
      }
    }

Creating a ConfigMap via kubectl

From literals (quick testing):

kubectl create configmap my-app-config \
  --from-literal=LOG_LEVEL=info \
  --from-literal=APP_PORT=8080 \
  --from-literal=DATABASE_HOST=postgres-service

From a file:

# Creates a key named "app.properties" with the file contents as the value
kubectl create configmap my-app-config --from-file=app.properties

From a directory (all files in the directory become keys):

kubectl create configmap my-app-config --from-file=./config-dir/

From a YAML manifest:

kubectl apply -f configmap.yaml

Verify the ConfigMap was created:

kubectl get configmap my-app-config -o yaml

Ways to Consume a ConfigMap in a Pod

There are three primary ways to consume ConfigMap data inside a pod.


Method 1: Environment Variables (Individual Keys)

Inject specific keys from a ConfigMap as individual environment variables.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: my-app:1.0
          env:
            - name: LOG_LEVEL          # env var name inside the container
              valueFrom:
                configMapKeyRef:
                  name: my-app-config  # ConfigMap name
                  key: LOG_LEVEL       # key inside the ConfigMap

            - name: APP_PORT
              valueFrom:
                configMapKeyRef:
                  name: my-app-config
                  key: APP_PORT

            - name: DATABASE_HOST
              valueFrom:
                configMapKeyRef:
                  name: my-app-config
                  key: DATABASE_HOST

Inside the container, your app reads these as standard OS environment variables:

echo $LOG_LEVEL       # → info
echo $APP_PORT        # → 8080
echo $DATABASE_HOST   # → postgres-service.default.svc.cluster.local

Method 2: envFrom (All Keys at Once)

Load all key-value pairs from a ConfigMap as environment variables in one shot.

spec:
  containers:
    - name: my-app
      image: my-app:1.0
      envFrom:
        - configMapRef:
            name: my-app-config       # All keys become env vars automatically

Pros: Simple — no need to list every key individually. Cons: All keys are exposed. If a key has an invalid env var name (e.g., contains hyphens or dots), it is silently skipped.


Method 3: Volume Mount (Files)

Mount ConfigMap data as files inside the container’s filesystem. This is ideal for full configuration files like nginx.conf, application.properties, or .env files.

spec:
  containers:
    - name: my-app
      image: my-app:1.0
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config          # Directory inside container
          readOnly: true

  volumes:
    - name: config-volume
      configMap:
        name: my-app-config              # Each key becomes a file

With the ConfigMap defined earlier, this creates the following inside the container:

/etc/config/
  ├── LOG_LEVEL           → contains: "info"
  ├── APP_PORT            → contains: "8080"
  ├── DATABASE_HOST       → contains: "postgres-service..."
  ├── app.properties      → contains the full properties file
  └── nginx.conf          → contains the full nginx config

Mounting a specific key as a specific file path:

volumes:
  - name: config-volume
    configMap:
      name: my-app-config
      items:
        - key: nginx.conf
          path: nginx.conf              # file will be at /etc/nginx/nginx.conf

Real-World Example: Node.js App with ConfigMap

Let’s walk through a complete, realistic example of deploying a Node.js Express API that reads its configuration from a ConfigMap.

Scenario

We have a Node.js app that connects to a PostgreSQL database. We want to manage the database host, port, database name, app port, and log level through a ConfigMap.


Step 1: Create the ConfigMap

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nodejs-app-config
  namespace: default
data:
  APP_PORT: "3000"
  LOG_LEVEL: "info"
  NODE_ENV: "production"
  DB_HOST: "postgres-service"
  DB_PORT: "5432"
  DB_NAME: "myappdb"
  DB_MAX_CONNECTIONS: "10"

Apply it:

kubectl apply -f configmap.yaml
# Output: configmap/nodejs-app-config created

Step 2: Create the Deployment

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs-api
  namespace: default
  labels:
    app: nodejs-api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nodejs-api
  template:
    metadata:
      labels:
        app: nodejs-api
    spec:
      containers:
        - name: nodejs-api
          image: myrepo/nodejs-api:1.2.0
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: nodejs-app-config
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10

Apply it:

kubectl apply -f deployment.yaml
# Output: deployment.apps/nodejs-api created

Step 3: Verify the Pod Reads ConfigMap Values

Check that the pods are running:

kubectl get pods -l app=nodejs-api
# NAME                          READY   STATUS    RESTARTS   AGE
# nodejs-api-7d9f6c8b4d-k2xlt   1/1     Running   0          45s
# nodejs-api-7d9f6c8b4d-p8nqz   1/1     Running   0          45s

Exec into a pod and verify the environment variables:

kubectl exec -it nodejs-api-7d9f6c8b4d-k2xlt -- env | grep -E "APP_PORT|LOG_LEVEL|DB_HOST"
# APP_PORT=3000
# LOG_LEVEL=info
# DB_HOST=postgres-service

Step 4: Update the ConfigMap

Suppose we want to change the log level to debug for troubleshooting:

kubectl edit configmap nodejs-app-config
# Change LOG_LEVEL from "info" to "debug" in the editor, then save

Or via patch:

kubectl patch configmap nodejs-app-config \
  --type merge \
  -p '{"data": {"LOG_LEVEL": "debug"}}'

Important: Pods using envFrom or env.valueFrom do NOT automatically pick up ConfigMap updates. You need to restart the deployment:

kubectl rollout restart deployment/nodejs-api
# Output: deployment.apps/nodejs-api restarted

Pods using volume mounts will eventually receive updated file contents (typically within 60 seconds), but the application must re-read the file to pick up changes.


Troubleshooting ConfigMap Issues

Here are the most common ConfigMap-related problems and how to diagnose and fix them in real environments.


Problem 1: Pod Stuck in CreateContainerConfigError

Symptom:

kubectl get pods
# NAME                          READY   STATUS                       RESTARTS   AGE
# nodejs-api-7d9f6c8b4d-k2xlt   0/1     CreateContainerConfigError   0          10s

Diagnosis:

kubectl describe pod nodejs-api-7d9f6c8b4d-k2xlt

Look for an Events section like:

Events:
  Warning  Failed   5s   kubelet   Error: configmap "nodejs-app-config" not found

Root Cause: The pod references a ConfigMap that doesn’t exist yet in the same namespace.

Fix:

# Check which namespace your pod is in
kubectl get pod nodejs-api-7d9f6c8b4d-k2xlt -o jsonpath='{.metadata.namespace}'
# default

# Check if the ConfigMap exists in that namespace
kubectl get configmap -n default
# If it's missing, create it:
kubectl apply -f configmap.yaml -n default

Problem 2: Environment Variable Has Wrong or Empty Value

Symptom: Application behaves unexpectedly — wrong database host, wrong port, missing feature flag.

Diagnosis:

# Inspect the running pod's actual environment
kubectl exec -it nodejs-api-7d9f6c8b4d-k2xlt -- env | grep DB_HOST
# DB_HOST=          <-- empty!
# Check the ConfigMap contents
kubectl get configmap nodejs-app-config -o yaml

Example Output (problem visible):

data:
  DB_HOST: ""        # ← empty value — this is the bug
  DB_PORT: "5432"

Root Cause options:

  • ConfigMap key has an empty value.
  • Key name mismatch (typo in ConfigMap key vs pod spec reference).
  • Pod is using a cached, stale ConfigMap (not yet restarted after update).

Fix:

# Edit the ConfigMap directly
kubectl edit configmap nodejs-app-config
# Fix the empty DB_HOST value, save

# Restart the deployment to pick up the change
kubectl rollout restart deployment/nodejs-api

Problem 3: Pod Cannot Find a Specific Key (configMapKeyRef)

Symptom:

kubectl describe pod nodejs-api-7d9f6c8b4d-k2xlt

Events:
  Warning  Failed  3s  kubelet  Error: couldn't find key LOG_LEVEL in ConfigMap default/nodejs-app-config

Root Cause: The pod’s env.valueFrom.configMapKeyRef.key value (LOG_LEVEL) does not exist as a key in the ConfigMap’s data section.

Diagnosis:

# List all keys in the ConfigMap
kubectl get configmap nodejs-app-config -o jsonpath='{.data}' | python3 -m json.tool
# {
#   "App_Port": "3000",     ← notice: wrong case!
#   "log_level": "info",    ← lowercase, but pod spec references "LOG_LEVEL"
#   "DB_HOST": "postgres-service"
# }

Fix:

Option A — Fix the ConfigMap key to match what the pod expects:

kubectl patch configmap nodejs-app-config \
  --type merge \
  -p '{"data": {"LOG_LEVEL": "info"}}'

Option B — Fix the pod spec to match the actual key name (update the Deployment YAML and re-apply).


Problem 4: Volume-Mounted ConfigMap File Not Updating

Symptom: You updated a ConfigMap but the application inside the pod is still reading the old configuration file.

Diagnosis:

# Check the file inside the container
kubectl exec -it nodejs-api-7d9f6c8b4d-k2xlt -- cat /etc/config/app.properties
# Still shows old values

# Check if Kubernetes has updated the file (uses symlinks internally)
kubectl exec -it nodejs-api-7d9f6c8b4d-k2xlt -- ls -la /etc/config/
# lrwxrwxrwx  app.properties -> ..data/app.properties
# The symlink updates when kubelet syncs — default sync period is 60s

Root Cause options:

  • Kubelet sync period hasn’t elapsed yet (default: 60 seconds). Wait and check again.
  • The application is caching the file in memory at startup and not re-reading it.
  • The ConfigMap is immutable (.immutable: true) — immutable ConfigMaps cannot be updated.

Fix:

# Check if ConfigMap is immutable
kubectl get configmap nodejs-app-config -o jsonpath='{.immutable}'
# true  ← this is why updates aren't working

# Immutable ConfigMaps CANNOT be edited. You must delete and recreate:
kubectl delete configmap nodejs-app-config
kubectl apply -f configmap-updated.yaml

# Then restart the pod to remount:
kubectl rollout restart deployment/nodejs-api

Problem 5: ConfigMap Exceeds 1 MiB Size Limit

Symptom:

kubectl apply -f large-configmap.yaml
# Error from server: etcd: request is too large

Diagnosis:

# Check the size of your ConfigMap YAML
wc -c configmap.yaml
# 1200000 bytes — over 1 MiB

Fix Options:

  • Break the ConfigMap into multiple smaller ConfigMaps.
  • Store large files in a PersistentVolume and mount it instead.
  • Use an external configuration service (HashiCorp Vault, AWS AppConfig, GCP Secret Manager).

Problem 6: ConfigMap Updates Not Reflected After kubectl rollout restart

Symptom: You updated the ConfigMap, restarted the deployment, but pods still show the old values.

Diagnosis:

# Verify the ConfigMap has the new value
kubectl get configmap nodejs-app-config -o jsonpath='{.data.LOG_LEVEL}'
# debug   ← correctly updated

# Check the new pod (post-restart)
kubectl get pods -l app=nodejs-api
# Note the new pod name (age will be recent)

kubectl exec -it nodejs-api-NEW-POD-NAME -- env | grep LOG_LEVEL
# LOG_LEVEL=info  ← still old value!

Root Cause: The deployment’s Pod template was not changed, so Kubernetes may have scheduled the new pod using a cached version of the ConfigMap from etcd if the pod started before the ConfigMap update fully propagated.

Fix:

# Force a full rollout by adding an annotation to the pod template
kubectl patch deployment nodejs-api \
  -p '{"spec":{"template":{"metadata":{"annotations":{"kubectl.kubernetes.io/restartedAt":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}}}}}'

This forces the pod template hash to change, guaranteeing new pods are created.


Quick Troubleshooting Reference

SymptomFirst Command to RunLikely Cause
CreateContainerConfigErrorkubectl describe pod <pod>ConfigMap does not exist
Empty env varkubectl exec <pod> -- envEmpty value or key mismatch in ConfigMap
Key not found errorkubectl get cm <name> -o yamlKey name typo or wrong case
Volume file not updatedkubectl exec <pod> -- cat <file>Immutable CM, or app caching config
Size limit errorwc -c configmap.yamlConfigMap over 1 MiB
Wrong namespacekubectl get cm -ACM in wrong namespace

Best Practices

1. Always use namespaces ConfigMaps are namespace-scoped. Always specify the namespace explicitly to avoid cross-environment confusion:

kubectl apply -f configmap.yaml -n production

2. Use immutable: true for stable configs Immutable ConfigMaps improve cluster performance (kubelet doesn’t watch them for changes) and prevent accidental updates:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v1
immutable: true
data:
  APP_VERSION: "1.2.0"

3. Name ConfigMaps with version suffixes for immutable configs Since immutable ConfigMaps can’t be updated, use versioned names and update the Deployment to reference the new version:

app-config-v1 → app-config-v2 → app-config-v3

4. Never store secrets in ConfigMaps ConfigMap data is stored in plain text in etcd. Use Secret objects for passwords, tokens, certificates, and API keys.

5. Validate ConfigMaps before applying

kubectl apply -f configmap.yaml --dry-run=client

6. Label your ConfigMaps for easy management

metadata:
  labels:
    app: nodejs-api
    env: production
    version: "1.2"

7. Use envFrom for simplicity, env.valueFrom for control

  • envFrom: Load all keys — great for apps that read everything from env.
  • env.valueFrom: Load specific keys — better when you need to rename keys or map only a subset.

8. Document your ConfigMap keys Add comments in the YAML to explain what each key does, especially for less obvious settings.


Summary

ConceptDetail
WhatKubernetes object to store non-sensitive config as key-value pairs
WhyDecouples config from images; enables environment portability
HowStored in etcd; injected into pods via env vars or volume mounts
WhereUsed in any workload: Deployments, StatefulSets, DaemonSets, Jobs
Max Size1 MiB
ScopeNamespace-scoped
Sensitive DataUse Secret instead
Auto-UpdatesVolume mounts update within ~60s; env vars require pod restart
ImmutabilitySet immutable: true to prevent changes and improve performance

ConfigMaps are a foundational Kubernetes concept. Mastering them gives you clean, portable, and manageable application configuration across all environments in your cluster.


Next: Kubernetes Secrets — Managing Sensitive Configuration Data