Module 4.1: ConfigMaps
Complexity:
[MEDIUM]- Multiple ways to create and consumeTime to Complete: 40-50 minutes
Prerequisites: Module 1.1 (Pods), understanding of environment variables
Learning Outcomes
Section titled “Learning Outcomes”After completing this module, you will be able to:
- Create ConfigMaps from literals, files, and directories using imperative and declarative methods
- Configure pods to consume ConfigMaps as environment variables and volume mounts
- Debug application configuration issues caused by missing or incorrectly mounted ConfigMaps
- Explain when to use environment variables vs volume mounts for configuration injection
Why This Module Matters
Section titled “Why This Module Matters”ConfigMaps decouple configuration from container images. Instead of baking settings into your image, you inject them at runtime. This lets you use the same image across environments (dev, staging, production) with different configurations.
The CKAD exam frequently tests ConfigMaps because they’re fundamental to the twelve-factor app methodology. Expect questions on:
- Creating ConfigMaps from literals, files, and directories
- Consuming as environment variables
- Mounting as volumes
- Updating configurations
The Restaurant Menu Analogy
Think of ConfigMaps as a restaurant’s specials board. The kitchen (container image) stays the same, but the specials (configuration) change daily. The chef doesn’t rebuild the kitchen to change the menu—they just update the board. ConfigMaps work the same way: change the config, restart the pod, get new behavior.
Creating ConfigMaps
Section titled “Creating ConfigMaps”From Literals
Section titled “From Literals”# Single key-valuek create configmap app-config --from-literal=APP_ENV=production
# Multiple key-valuesk create configmap app-config \ --from-literal=APP_ENV=production \ --from-literal=LOG_LEVEL=info \ --from-literal=MAX_CONNECTIONS=100
# View the resultk get configmap app-config -o yamlFrom Files
Section titled “From Files”# Create a config fileecho "database.host=db.example.comdatabase.port=5432database.name=myapp" > app.properties
# Create ConfigMap from filek create configmap app-config --from-file=app.properties
# Custom key namek create configmap app-config --from-file=config.properties=app.properties
# Multiple filesk create configmap app-config \ --from-file=app.properties \ --from-file=logging.propertiesFrom Directories
Section titled “From Directories”# All files in directory become keysk create configmap app-config --from-file=./config-dir/From YAML
Section titled “From YAML”apiVersion: v1kind: ConfigMapmetadata: name: app-configdata: APP_ENV: production LOG_LEVEL: info app.properties: | database.host=db.example.com database.port=5432 database.name=myappConsuming ConfigMaps
Section titled “Consuming ConfigMaps”As Environment Variables
Section titled “As Environment Variables”Single variable:
apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: nginx env: - name: APP_ENVIRONMENT valueFrom: configMapKeyRef: name: app-config key: APP_ENVAll keys as variables:
apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: nginx envFrom: - configMapRef: name: app-configPause and predict: You update a ConfigMap that a pod consumes via
envFrom. Will the pod automatically pick up the new values? What if it was mounted as a volume instead?
As Volume Files
Section titled “As Volume Files”Mount entire ConfigMap:
apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: nginx volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: app-configMount specific keys:
apiVersion: v1kind: Podmetadata: name: appspec: containers: - name: app image: nginx volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: app-config items: - key: app.properties path: application.propertiesStop and think: When you mount a ConfigMap as a volume at
/etc/config, what happens to any files that were already in/etc/configinside the container image? How would you avoid this problem?
Mount to specific file:
volumeMounts:- name: config-volume mountPath: /etc/config/app.conf subPath: app.propertiesConfigMap Patterns
Section titled “ConfigMap Patterns”Environment-Specific Config
Section titled “Environment-Specific Config”# Developmentk create configmap app-config \ --from-literal=APP_ENV=development \ --from-literal=DEBUG=true \ -n development
# Productionk create configmap app-config \ --from-literal=APP_ENV=production \ --from-literal=DEBUG=false \ -n productionConfiguration Files
Section titled “Configuration Files”# nginx.confcat << 'EOF' > nginx.confserver { listen 80; server_name localhost; location / { root /usr/share/nginx/html; }}EOF
k create configmap nginx-config --from-file=nginx.conf
# Mount in podcat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: nginx-customspec: containers: - name: nginx image: nginx volumeMounts: - name: config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf volumes: - name: config configMap: name: nginx-configEOFConfigMap Updates
Section titled “ConfigMap Updates”Behavior by Consumption Method
Section titled “Behavior by Consumption Method”| Method | Update Behavior |
|---|---|
| Environment variables | NOT updated - requires pod restart |
| Volume mounts | Updated automatically (kubelet sync period ~1 min) |
| subPath mounts | NOT updated - requires pod restart |
Forcing Updates
Section titled “Forcing Updates”# Restart pods to pick up env var changesk rollout restart deployment/myapp
# For volume-mounted configs, wait or force sync# Pods auto-update within kubelet sync periodVisualization
Section titled “Visualization”┌─────────────────────────────────────────────────────────────┐│ ConfigMap Usage │├─────────────────────────────────────────────────────────────┤│ ││ ConfigMap: app-config ││ ┌─────────────────────────────────────┐ ││ │ APP_ENV: production │ ││ │ LOG_LEVEL: info │ ││ │ app.properties: | │ ││ │ database.host=db.example.com │ ││ │ database.port=5432 │ ││ └─────────────────────────────────────┘ ││ │ │ ││ ▼ ▼ ││ ┌──────────────┐ ┌──────────────┐ ││ │ envFrom │ │ volume │ ││ │ │ │ mount │ ││ │ $APP_ENV │ │ │ ││ │ $LOG_LEVEL │ │ /etc/config/ │ ││ └──────────────┘ │ app.properties│ ││ └──────────────┘ ││ │└─────────────────────────────────────────────────────────────┘Quick Reference
Section titled “Quick Reference”# Createk create configmap NAME --from-literal=KEY=VALUEk create configmap NAME --from-file=FILEk create configmap NAME --from-file=DIR/
# Viewk get configmap NAME -o yamlk describe configmap NAME
# Editk edit configmap NAME
# Deletek delete configmap NAMEDid You Know?
Section titled “Did You Know?”-
ConfigMaps have a 1MB size limit. For larger configurations, consider mounting external storage or using init containers.
-
ConfigMap data is stored in etcd unencrypted (unlike Secrets which can be encrypted at rest). Don’t put sensitive data in ConfigMaps.
-
The
immutable: truefield (Kubernetes 1.21+) prevents accidental changes and improves cluster performance by reducing watch load.
From the Trenches: The “Masked Directory” Outage
Section titled “From the Trenches: The “Masked Directory” Outage”A platform team was deploying a new logging agent DaemonSet across a production cluster. The agent required a custom configuration file, which the team provided via a ConfigMap. To inject it, they mounted the ConfigMap volume directly to /etc/agent/ where the agent looked for its configuration.
As soon as the DaemonSet was applied, the logging agent crashed cluster-wide. The logs showed FATAL: Missing required default certificates.
The post-mortem revealed the root cause: The base container image included critical default files (like internal certificates and base configuration) baked into the /etc/agent/ directory. By mounting the ConfigMap volume directly to the directory path (mountPath: /etc/agent/), Kubernetes effectively “masked” all the existing contents of that directory with the contents of the ConfigMap. The agent couldn’t find its certificates and crashed. The team fixed the outage by using a subPath mount (mountPath: /etc/agent/custom.conf with subPath: custom.conf), which injects only the specific file into the directory without hiding the existing baked-in files.
Common Mistakes
Section titled “Common Mistakes”| Mistake | Why It Hurts | Solution |
|---|---|---|
| Expecting env vars to update | App uses stale config | Restart pod after ConfigMap changes |
| Using subPath expecting updates | subPath doesn’t auto-update | Use full volume mount or restart |
| Storing secrets in ConfigMaps | Data visible in plain text | Use Secrets for sensitive data |
| Not namespacing ConfigMaps | Config leaks across environments | Create per-namespace ConfigMaps |
| Typo in key name | Pod won’t start or gets wrong config | Use k describe cm to verify |
-
A developer updates a ConfigMap with new database connection settings. They confirm the ConfigMap is correct with
kubectl get cm -o yaml. But their pod still uses the old values. The pod consumes the ConfigMap viaenvFrom. What is happening and how do they fix it?Answer
Environment variables from ConfigMaps are injected at pod startup time and are never updated afterward, even if the underlying ConfigMap changes. The developer must restart the pods to pick up the new values — for a Deployment, `kubectl rollout restart deployment/name` is the cleanest approach. If the team needs live config reloading, they should switch to volume-mounted ConfigMaps, which the kubelet automatically syncs within ~1 minute. However, the application must also be written to watch for file changes and reload. -
You mount a ConfigMap as a volume at
/etc/nginx/conf.d/. After the pod starts, nginx fails because it can’t find its default configuration files. Before adding the ConfigMap mount, the directory haddefault.confprovided by the nginx image. What went wrong?Answer
Mounting a ConfigMap (or any volume) at a directory path completely replaces the directory's contents. The original files from the container image at `/etc/nginx/conf.d/` are hidden by the mount. The fix is to use `subPath` to mount a specific file instead of the entire directory: `mountPath: /etc/nginx/conf.d/my-custom.conf` with `subPath: my-custom.conf`. This preserves the existing files in the directory. The trade-off is that subPath mounts do not auto-update when the ConfigMap changes. -
A team stores their database password in a ConfigMap alongside other non-sensitive application settings. During a security audit, this is flagged. Why is this a problem, and what should they do differently?
Answer
ConfigMap data is stored as plain text in etcd and is visible to anyone who can run `kubectl get cm -o yaml`. It also appears unobfuscated in `kubectl describe`, pod environment listings, and API responses. Sensitive data like passwords should be stored in Secrets, which provide base64 encoding (not encryption, but not human-readable at a glance), can be encrypted at rest with EncryptionConfiguration, and have stricter RBAC controls. Move the password to a Secret and reference it via `secretKeyRef` in the pod spec, keeping only non-sensitive settings in the ConfigMap. -
You create a ConfigMap from a file:
kubectl create configmap app-config --from-file=settings.conf. When you mount it as a volume, you expect a file calledsettings.confat the mount path. Instead, the application can’t find it. You check and the file is there but the application config parser is looking forapp.conf. How do you make the ConfigMap mount use a different filename?Answer
Use the `items` field in the volume definition to remap the key to a different file path: `volumes: - name: config, configMap: name: app-config, items: - key: settings.conf, path: app.conf`. This tells Kubernetes to take the ConfigMap key `settings.conf` and project it as a file named `app.conf` in the mounted volume directory. Alternatively, you could create the ConfigMap with a custom key name from the start: `kubectl create configmap app-config --from-file=app.conf=settings.conf`. Either approach solves the file path discrepancy at the infrastructure level rather than requiring an application code change.
Hands-On Exercise
Section titled “Hands-On Exercise”Task: Create and consume ConfigMaps multiple ways.
Setup:
# Create ConfigMap from literalsk create configmap web-config \ --from-literal=APP_COLOR=blue \ --from-literal=APP_MODE=production
# Verifyk get configmap web-config -o yamlPart 1: Environment Variables
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: env-podspec: containers: - name: app image: busybox command: ['sh', '-c', 'echo Color: $APP_COLOR, Mode: $APP_MODE && sleep 3600'] envFrom: - configMapRef: name: web-configEOF
# Verify environmentk logs env-podPart 2: Volume Mount
# Create config filek create configmap nginx-index --from-literal=index.html='<h1>Hello from ConfigMap</h1>'
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: vol-podspec: containers: - name: nginx image: nginx volumeMounts: - name: html mountPath: /usr/share/nginx/html volumes: - name: html configMap: name: nginx-indexEOF
# Testk exec vol-pod -- cat /usr/share/nginx/html/index.htmlCleanup:
k delete pod env-pod vol-podk delete configmap web-config nginx-indexPractice Drills
Section titled “Practice Drills”Drill 1: Create from Literals (Target: 1 minute)
Section titled “Drill 1: Create from Literals (Target: 1 minute)”k create configmap drill1 --from-literal=KEY1=value1 --from-literal=KEY2=value2k get cm drill1 -o yamlk delete cm drill1Drill 2: Create from File (Target: 2 minutes)
Section titled “Drill 2: Create from File (Target: 2 minutes)”echo "setting1=onsetting2=off" > /tmp/settings.conf
k create configmap drill2 --from-file=/tmp/settings.confk get cm drill2 -o yamlk delete cm drill2Drill 3: Environment Variables (Target: 3 minutes)
Section titled “Drill 3: Environment Variables (Target: 3 minutes)”k create configmap drill3 --from-literal=DB_HOST=localhost --from-literal=DB_PORT=5432
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill3spec: containers: - name: app image: busybox command: ['sh', '-c', 'env | grep DB && sleep 3600'] envFrom: - configMapRef: name: drill3EOF
k logs drill3k delete pod drill3 cm drill3Drill 4: Volume Mount (Target: 3 minutes)
Section titled “Drill 4: Volume Mount (Target: 3 minutes)”k create configmap drill4 --from-literal=config.json='{"debug": true}'
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill4spec: containers: - name: app image: busybox command: ['sh', '-c', 'cat /config/config.json && sleep 3600'] volumeMounts: - name: cfg mountPath: /config volumes: - name: cfg configMap: name: drill4EOF
k logs drill4k delete pod drill4 cm drill4Drill 5: Specific Key Mount (Target: 3 minutes)
Section titled “Drill 5: Specific Key Mount (Target: 3 minutes)”k create configmap drill5 \ --from-literal=app.conf='main config' \ --from-literal=log.conf='log config'
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill5spec: containers: - name: app image: busybox command: ['sh', '-c', 'ls /config && cat /config/application.conf && sleep 3600'] volumeMounts: - name: cfg mountPath: /config volumes: - name: cfg configMap: name: drill5 items: - key: app.conf path: application.confEOF
k logs drill5k delete pod drill5 cm drill5Drill 6: Complete Scenario (Target: 5 minutes)
Section titled “Drill 6: Complete Scenario (Target: 5 minutes)”Scenario: Deploy nginx with custom configuration.
# Create nginx configcat << 'NGINX' > /tmp/nginx.confserver { listen 8080; location / { return 200 'Custom Config Works!\n'; add_header Content-Type text/plain; }}NGINX
k create configmap drill6-nginx --from-file=/tmp/nginx.conf
cat << 'EOF' | k apply -f -apiVersion: v1kind: Podmetadata: name: drill6spec: containers: - name: nginx image: nginx ports: - containerPort: 8080 volumeMounts: - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf volumes: - name: nginx-config configMap: name: drill6-nginxEOF
# Test (wait for pod ready)k wait --for=condition=Ready pod/drill6 --timeout=30sk exec drill6 -- curl -s localhost:8080
k delete pod drill6 cm drill6-nginxNext Module
Section titled “Next Module”Module 4.2: Secrets - Manage sensitive data securely.