| Tested on | Rocky Linux 10.2 (Red Quartz) workstation |
|---|---|
| Package | kubectl 1.36.3 |
| Applies to | Any host with kubectl configured; any Kubernetes cluster |
| Cert prep | CKA · CKAD |
| Lab environment | Multi-node kubeadm cluster with containerd — install Kubernetes with kubeadm |
| Privilege | Normal user (no sudo required on the workstation) |
| Scope | Create ConfigMaps from literals, files, env files, and YAML; inject one key as an environment variable; import all keys with envFrom; mount keys as read-only files with optional items; and mark ConfigMaps immutable. Does not cover detailed rollout automation, application reload controllers, or advanced update-propagation tuning, or Secret creation. |
| Related guides | Kubernetes volumes |
A ConfigMap keeps application settings out of the container image so the same build can run in development, staging, and production. This walkthrough uses one small app configuration—APP_MODE, LOG_LEVEL, and an application.properties file—and consumes it three ways: one mapped environment variable, every key through envFrom, and files on a read-only volume.
What Is a Kubernetes ConfigMap?
A ConfigMap stores non-confidential configuration as key-value data. Pods can read those keys as environment variables, command arguments, or files on a volume. ConfigMaps are namespace-scoped, and the ConfigMap must exist in the same namespace as the Pod that references it.
Use Kubernetes Secrets for credentials, tokens, and private keys. ConfigMaps are for settings that are safe to store in plain text.
ConfigMaps expose two data fields:
| Field | Intended content |
|---|---|
data |
UTF-8 text values |
binaryData |
Base64-encoded binary values |
This article uses data only. binaryData follows the same consumption patterns when you need to ship small binary blobs such as a TLS certificate bundle without putting raw bytes in data.
A ConfigMap cannot contain more than 1 MiB of data. Use a persistent volume, object storage, database, or file service for larger configuration assets.
Create a dedicated namespace for the examples:
kubectl create namespace configmap-labSample output:
namespace/configmap-lab createdCreate ConfigMaps
Create from literal values
Literal values suit a handful of scalar settings. Each --from-literal flag becomes one key in the ConfigMap.
kubectl create configmap app-config -n configmap-lab --from-literal=APP_MODE=development --from-literal=LOG_LEVEL=infoSample output:
configmap/app-config createdRepeated --from-literal options add multiple keys in a single object. Confirm the ConfigMap exists:
kubectl get configmap app-config -n configmap-labSample output:
NAME DATA AGE
app-config 2 0sThe DATA column shows two keys. Inspect the stored values:
kubectl describe configmap app-config -n configmap-labSample output:
Name: app-config
Namespace: configmap-lab
Labels: <none>
Annotations: <none>
Data
====
APP_MODE:
----
development
LOG_LEVEL:
----
info
BinaryData
====
Events: <none>For a manifest-friendly view, dump the live object:
kubectl get configmap app-config -n configmap-lab -o yamlSample output:
apiVersion: v1
data:
APP_MODE: development
LOG_LEVEL: info
kind: ConfigMap
metadata:
creationTimestamp: "2026-07-26T15:41:52Z"
name: app-config
namespace: configmap-lab
resourceVersion: "459899"
uid: c8c738a7-2e63-4c38-b106-0404e450a421The data map holds the keys you passed on the command line.
Create from files and directories
File-based creation is the usual path for configuration files. Start with a sample properties file on your workstation:
server.port=8080
app.name=demoSave it as application.properties, then create a ConfigMap where the filename becomes the key:
kubectl create configmap app-files -n configmap-lab --from-file=application.propertiesSample output:
configmap/app-files createdWhen you omit an explicit key name, Kubernetes uses the basename of the file as the ConfigMap key. To store the same file under a different key, use key=path syntax:
kubectl create configmap app-files-keyed -n configmap-lab --from-file=app.properties=application.propertiesSample output:
configmap/app-files-keyed createdCompare the resulting keys:
kubectl get configmap app-files -n configmap-lab -o yamlSample output:
apiVersion: v1
data:
application.properties: |
server.port=8080
app.name=demo
kind: ConfigMap
metadata:
name: app-files
namespace: configmap-labThe first object uses the filename as the key. Dump the keyed variant to see the difference:
kubectl get configmap app-files-keyed -n configmap-lab -o yamlSample output:
apiVersion: v1
data:
app.properties: |
server.port=8080
app.name=demo
kind: ConfigMap
metadata:
name: app-files-keyed
namespace: configmap-labThe file content becomes the value. With app.properties=application.properties, the key is app.properties even though the source file on disk keeps its original name.
Point --from-file at a directory to load every regular file as a separate key. Create the directory and sample files first:
mkdir -p config-dirprintf 'database.host=db.example\n' > config-dir/database.confprintf 'log.level=info\n' > config-dir/logging.confThen create the ConfigMap:
kubectl create configmap app-dir -n configmap-lab --from-file=./config-dir/Sample output:
configmap/app-dir createdEach regular file whose basename is a valid ConfigMap key becomes one entry. Subdirectories, symbolic links, devices, and other non-regular entries are ignored.
Create from an environment file
An env file is a line-oriented KEY=value list. Create app.env:
APP_MODE=production
LOG_LEVEL=warningImport it with --from-env-file:
kubectl create configmap app-env -n configmap-lab --from-env-file=app.envSample output:
configmap/app-env created--from-env-file reads line-oriented KEY=value entries. Blank lines and lines beginning with # are ignored, while quotation marks are preserved as part of the value. Use --from-file when JSON, YAML, properties, or another configuration file should remain one complete ConfigMap value.
Define a ConfigMap in YAML
Declarative manifests are easier to review in Git and apply with kubectl apply. The example below mixes scalar keys with a multiline configuration file:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-yaml
namespace: configmap-lab
data:
APP_MODE: staging
LOG_LEVEL: debug
application.properties: |
server.port=9090
app.name=demo-yamlApply the manifest:
kubectl apply -f app-config-yaml.yamlSample output:
configmap/app-config-yaml createdThe pipe (|) block under application.properties preserves newlines so the mounted file keeps its structure.
For binary content, use binaryData with base64-encoded values instead of placing raw bytes in data.
Generate YAML with client-side dry run
Generate reusable YAML from imperative input without creating the object:
kubectl create configmap app-config-dry -n configmap-lab --from-literal=APP_MODE=production --dry-run=client -o yamlSample output:
apiVersion: v1
data:
APP_MODE: production
kind: ConfigMap
metadata:
name: app-config-dry
namespace: configmap-labRedirect that output into a file when you want a starting manifest for version control.
Use ConfigMaps as Environment Variables
Inject one key with configMapKeyRef
Use env[].valueFrom.configMapKeyRef when the application expects one variable with a specific name. The container variable does not have to match the ConfigMap key.
The Pod below maps ConfigMap key LOG_LEVEL to environment variable APP_LOG_LEVEL:
apiVersion: v1
kind: Pod
metadata:
name: env-single
namespace: configmap-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo APP_LOG_LEVEL=$APP_LOG_LEVEL; sleep 3600"]
env:
- name: APP_LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
restartPolicy: NeverApply the Pod:
kubectl apply -f env-single.yamlSample output:
pod/env-single createdWait until the container is ready before checking variables:
kubectl wait --for=condition=Ready pod/env-single -n configmap-lab --timeout=60sSample output:
pod/env-single condition metRead the container log to confirm the mapping:
kubectl logs env-single -n configmap-labSample output:
APP_LOG_LEVEL=infoYou can also read the variable directly inside the container:
kubectl exec env-single -n configmap-lab -- printenv APP_LOG_LEVELSample output:
infoThe value info came from the LOG_LEVEL key in app-config, even though the process sees APP_LOG_LEVEL.
Import all keys with envFrom
When the application already expects environment variables that match ConfigMap keys, envFrom is shorter than listing each env entry.
apiVersion: v1
kind: Pod
metadata:
name: env-from
namespace: configmap-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "env | sort; sleep 3600"]
envFrom:
- configMapRef:
name: app-config
restartPolicy: NeverApply the Pod:
kubectl apply -f env-from.yamlSample output:
pod/env-from createdWait for Ready, then inspect the environment:
kubectl wait --for=condition=Ready pod/env-from -n configmap-lab --timeout=60sSample output:
pod/env-from condition metEvery suitable key in app-config becomes an environment variable with the same name. The log shows the imported values among the usual Kubernetes service variables:
kubectl logs env-from -n configmap-labSample output (trimmed):
APP_MODE=development
LOG_LEVEL=infoPrefer explicit env entries when you need to rename keys or import only a subset.
For how env and envFrom interact with command, args, and $(VAR) substitution, see commands, args and environment variables.
Add an envFrom prefix
When every variable should share a common prefix, add prefix alongside configMapRef. The prefix and configMapRef fields are siblings under an EnvFromSource:
envFrom:
- prefix: APP_
configMapRef:
name: app-configWith prefix: APP_, keys APP_MODE and LOG_LEVEL become environment variables APP_APP_MODE and APP_LOG_LEVEL.
Mount ConfigMaps as Files
Some applications read configuration from a file path rather than the environment. A ConfigMap volume turns each key into a filename and each value into file content. The mount is read-only.
Mount every key
Create a ConfigMap that includes scalar keys and a properties file, then mount it at /etc/app-config. Save both resources in volume-mount.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-mount-config
namespace: configmap-lab
data:
APP_MODE: production
LOG_LEVEL: warning
application.properties: |
server.port=8080
app.name=demo
---
apiVersion: v1
kind: Pod
metadata:
name: volume-mount
namespace: configmap-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
volumeMounts:
- name: app-config-vol
mountPath: /etc/app-config
volumes:
- name: app-config-vol
configMap:
name: app-mount-config
restartPolicy: NeverDefine the volume once at Pod scope. Every container that needs the files adds its own volumeMounts entry pointing at that volume name.
Apply the manifest:
kubectl apply -f volume-mount.yamlSample output:
configmap/app-mount-config created
pod/volume-mount createdWait for the Pod, then list the mounted files:
kubectl wait --for=condition=Ready pod/volume-mount -n configmap-lab --timeout=60sSample output:
pod/volume-mount condition metList the files under the mount path:
kubectl exec volume-mount -n configmap-lab -- ls -l /etc/app-configSample output:
total 0
lrwxrwxrwx 1 root root 15 Jul 26 15:42 APP_MODE -> ..data/APP_MODE
lrwxrwxrwx 1 root root 16 Jul 26 15:42 LOG_LEVEL -> ..data/LOG_LEVEL
lrwxrwxrwx 1 root root 29 Jul 26 15:42 application.properties -> ..data/application.propertiesRead the properties file the application would parse:
kubectl exec volume-mount -n configmap-lab -- cat /etc/app-config/application.propertiesSample output:
server.port=8080
app.name=demoSymlinks under ..data are normal—the kubelet projects ConfigMap keys as individual files in the mount path.
Select and rename keys
By default a ConfigMap volume exposes every key. Use volumes[].configMap.items to select keys and rename the target filename:
apiVersion: v1
kind: Pod
metadata:
name: volume-items
namespace: configmap-lab
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "ls -l /etc/app-config; cat /etc/app-config/app.properties; sleep 3600"]
volumeMounts:
- name: app-config-vol
mountPath: /etc/app-config
volumes:
- name: app-config-vol
configMap:
name: app-mount-config
items:
- key: application.properties
path: app.properties
mode: 0440
restartPolicy: NeverOnly application.properties is mounted, renamed to app.properties. Keys not listed in items do not appear in the directory.
Apply the Pod:
kubectl apply -f volume-items.yamlSample output:
pod/volume-items createdSet projected file permissions
Wait for the Pod before reading logs or checking permissions:
kubectl wait --for=condition=Ready pod/volume-items -n configmap-lab --timeout=60sSample output:
pod/volume-items condition metThe container startup command lists the mount and prints the file:
kubectl logs volume-items -n configmap-labSample output:
total 0
lrwxrwxrwx 1 root root 21 Jul 26 15:42 app.properties -> ..data/app.properties
server.port=8080
app.name=demoThe symlink shows lrwxrwxrwx, but the configured mode applies to the projected target file rather than the symlink itself. stat on the symlink path reports 777; verify the target file under ..data:
kubectl exec volume-items -n configmap-lab -- stat -c '%a %n' /etc/app-config/..data/app.propertiesSample output:
440 /etc/app-config/..data/app.propertiesAvoid hiding existing files with directory mounts
Volume mounts replace the entire mount path, so a full-directory mount can hide files baked into the image. When you need to overlay a single file without hiding the rest of a directory—such as replacing /etc/nginx/nginx.conf while keeping other files in /etc/nginx—use subPath on the volume mount. See subPath volume examples for that pattern.
Understand ConfigMap Update Behavior
ConfigMaps consumed through env or envFrom are captured when the container starts and require Pod replacement or restart to receive changed values. ConfigMaps mounted as full volumes are updated eventually by the kubelet, although the application may still need to reload the file. ConfigMap files mounted with subPath do not receive updates.
Full ConfigMap volume projections update eventually, environment variables do not, and subPath is excluded from automatic projection updates. For rollout timing, reload controllers, and propagation details, see update ConfigMaps and Secrets without Pod restart.
Create an Immutable ConfigMap
Set immutable: true when configuration should never change in place. Immutable ConfigMaps protect configuration from accidental in-place changes. On clusters with very large numbers of ConfigMap mounts, they can also reduce API-server load because kubelets do not need to watch those objects for data changes.
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config-immutable
namespace: configmap-lab
data:
APP_MODE: release
immutable: trueApply the manifest:
kubectl apply -f app-config-immutable.yamlSample output:
configmap/app-config-immutable createdAn attempt to patch data fails:
kubectl patch configmap app-config-immutable -n configmap-lab --type merge -p '{"data":{"APP_MODE":"changed"}}'Sample output:
The ConfigMap "app-config-immutable" is invalid: data: Forbidden: field is immutable when `immutable` is setYou cannot remove immutable from an existing ConfigMap. To ship new settings, create a ConfigMap with a new name and update the Pod or workload template to reference it.
Choose a Consumption Method and Fix Common Errors
| Requirement | Method |
|---|---|
| Inject one selected value | configMapKeyRef in env |
| Import many variables | envFrom with configMapRef |
| Provide a complete configuration file | ConfigMap volume |
| Select or rename mounted files | Volume items |
| Prevent in-place data changes | immutable: true |
| Symptom | Likely cause | Fix |
|---|---|---|
| ConfigMap not found | Wrong name or namespace | Confirm the object with kubectl get configmap -n <namespace>; create it in the Pod namespace before the Pod starts |
| Required key does not exist | Typo in configMapKeyRef.key or items[].key |
Run kubectl describe configmap <name> and match the key exactly |
| Mounted files hide existing image files | Volume mounted over a directory the image already uses | Mount into a dedicated path such as /etc/app-config, or use subPath for a single file |
| Application expects a file but receives an environment variable | Wrong consumption method | Switch from env to a ConfigMap volume, or the reverse |
| Immutable ConfigMap cannot be edited | immutable: true blocks updates |
Create a new ConfigMap name and update the workload reference |
What's Next
- Kubernetes Secrets with Examples
- Kubernetes Requests, Limits and QoS Classes
- Kubernetes ResourceQuota and LimitRange with Examples
References
- ConfigMaps — Kubernetes documentation
- Configure a Pod to use a ConfigMap — official task guide
- kubectl create configmap — generated command reference
Summary
You created ConfigMaps from literals, files, env files, and YAML, then verified keys with kubectl get, describe, and -o yaml. The same app-config object fed three consumption paths: configMapKeyRef mapped LOG_LEVEL to APP_LOG_LEVEL, envFrom imported every key as a matching environment variable, and a ConfigMap volume projected keys as read-only files under /etc/app-config.
Volume mounts replace the entire mount path, so a full-directory mount can hide files baked into the image. Use a dedicated directory or subPath when only one file should change. Immutable ConfigMaps reject in-place edits—plan versioned names and workload updates instead of patching data after the fact.
For credentials, switch to Secrets. For timing around rollouts after configuration changes, read how env vars, mounted files, and projected volumes pick up updates in the companion guide on updating ConfigMaps without unnecessary restarts.

