Kubernetes Entry Created: 10 Mar 2026 Updated: 10 Mar 2026

Kubernetes ConfigMaps

Overview

Every application needs configuration — database hostnames, log levels, feature flags, connection strings. Hard-coding these values into your container image is a problem because you would need a different image for development, staging, and production. That defeats one of the core benefits of containers: build once, run anywhere.

A ConfigMap is a Kubernetes object that stores non-sensitive configuration data as key-value pairs. You can think of it in two ways:

  1. As a small filesystem — each key becomes a file name and each value becomes the file content.
  2. As a set of environment variables — each key-value pair can be injected into a container's environment.

The key idea is that ConfigMaps are combined with a Pod right before it runs. This means the same container image and Pod definition can be reused across many workloads — only the ConfigMap changes. You get a clean separation between your application code and your runtime configuration.

Core Concepts

What Is a ConfigMap?

At its core, a ConfigMap is simply a dictionary of string key-value pairs stored as a Kubernetes object. Unlike Secrets (which are designed for sensitive data), ConfigMaps are intended for non-sensitive configuration — things like log levels, service URLs, feature toggles, and connection strings (without passwords).

A ConfigMap lives inside a Namespace. Pods in the same Namespace can reference it. Pods in a different Namespace cannot.

Why Not Just Use Environment Variables Directly?

You could define environment variables directly in a Pod spec, but that approach has drawbacks:

  1. Configuration is scattered throughout many Pod or Deployment manifests instead of being in one central place.
  2. Updating a single value means editing every manifest that uses it.
  3. There is no way to mount a whole configuration file (like appsettings.json) as an environment variable.

ConfigMaps solve all three problems. You define configuration once, reference it from as many Pods as you like, and update it in a single place.

Three Ways to Consume a ConfigMap

Kubernetes gives you three methods to pass ConfigMap data into a container:

MethodHow It WorksBest For
Environment VariablesEach key-value pair becomes an environment variable inside the container.Simple values: log levels, ports, feature flags.
Volume / FilesystemThe ConfigMap is mounted as a directory. Each key becomes a file; its value becomes the file content.Structured config files: appsettings.json, nginx.conf.
Command-Line ArgumentsEnvironment variables sourced from the ConfigMap are referenced in the container's command or args field using $(VAR_NAME) syntax.CLI applications that accept flags or positional arguments.

You can mix and match all three methods in the same Pod. We will use all of them in the step-by-step example below.

Hands-On: Kubernetes Commands

Creating a ConfigMap Imperatively (Command Line)

The fastest way to create a ConfigMap is with kubectl create configmap. You can feed it literal values, files, or even entire directories.

From Literal Values

Use --from-literal to pass individual key-value pairs directly on the command line:

kubectl create configmap app-settings \
--from-literal=LOG_LEVEL=Information \
--from-literal=MAX_RETRIES=5

This creates a ConfigMap named app-settings with two keys: LOG_LEVEL and MAX_RETRIES.

From a File

Suppose you have a configuration file called my-config.txt on disk:

# This is a sample config file that I might use to configure an application
parameter1 = value1
parameter2 = value2

You can load it into a ConfigMap with --from-file:

kubectl create configmap my-config \
--from-file=my-config.txt \
--from-literal=extra-param=extra-value \
--from-literal=another-param=another-value

The file name (my-config.txt) becomes the key, and the entire file content becomes the value. You can also combine --from-file and --from-literal in one command.

From a Directory

If you have an entire directory of config files, point --from-file at the directory:

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

Each file in that directory becomes a key in the ConfigMap.

Viewing a ConfigMap

To see the contents of a ConfigMap, use:

kubectl get configmap my-config -o yaml

This prints the full YAML representation including the data section. Here is an example of what the output looks like for the my-config ConfigMap we created above:

apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
namespace: default
data:
another-param: another-value
extra-param: extra-value
my-config.txt: |
# This is a sample config file that I might use to configure an application
parameter1 = value1
parameter2 = value2

Notice that the file content is stored verbatim under the key my-config.txt. The | symbol in YAML preserves newlines in multi-line values.

Describing a ConfigMap

For a quick summary without the raw YAML:

kubectl describe configmap my-config

Listing All ConfigMaps

kubectl get configmaps

Add -n <namespace> to list ConfigMaps in a specific Namespace, or --all-namespaces to list them across the entire cluster.

Editing a ConfigMap

To modify an existing ConfigMap in-place:

kubectl edit configmap my-config

This opens the ConfigMap in your default text editor. Save and close to apply changes. Note that running Pods do not automatically pick up environment variable changes — you need to restart them. Volume-mounted ConfigMaps, however, are updated automatically by the kubelet (with a short delay).

Deleting a ConfigMap

kubectl delete configmap my-config

Be careful: if any Pod references a deleted ConfigMap, that Pod will fail to start until the ConfigMap is recreated.

Step-by-Step Example

In this walkthrough, we will build a realistic scenario: a .NET Notification API that needs runtime configuration for SMTP settings, logging levels, and rate limiting. We will create two ConfigMaps and a Deployment that consumes them using environment variables and volume mounts.

Step 1: Define the Simple Key-Value ConfigMap

First, create a ConfigMap with simple key-value pairs for SMTP settings and logging. Save this file as notification-api-config.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
name: notification-api-config
data:
SMTP_HOST: "mail.internal.local"
SMTP_PORT: "587"
RETRY_COUNT: "3"
LOG_LEVEL: "Information"
ALLOWED_ORIGINS: "https://portal.example.com,https://admin.example.com"

Apply it to your cluster:

kubectl apply -f notification-api-config.yaml

Each key here is a simple string. All values in a ConfigMap must be strings — even numbers like "587" are stored as strings. Your application is responsible for parsing them into the correct type.

Step 2: Define a File-Based ConfigMap

Next, create a ConfigMap that holds an entire appsettings.Production.json file. This is useful when your application expects a structured configuration file at a specific path. Save this as notification-api-appsettings.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
name: notification-api-appsettings
data:
appsettings.Production.json: |
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Microsoft.AspNetCore": "Warning"
}
},
"SmtpSettings": {
"Host": "mail.internal.local",
"Port": 587,
"UseTls": true
},
"RateLimiting": {
"PermitLimit": 100,
"WindowSeconds": 60
}
}

Apply it:

kubectl apply -f notification-api-appsettings.yaml

The key appsettings.Production.json will become a file name when we mount this ConfigMap as a volume. The JSON content after the | character will become the content of that file. This means your .NET application can read it just like any other appsettings file.

Step 3: Create the Deployment That Uses Both ConfigMaps

Now create a Deployment that references both ConfigMaps. This manifest demonstrates all the consumption methods in a single resource. Save this as notification-api-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
name: notification-api
labels:
app: notification-api
spec:
replicas: 2
selector:
matchLabels:
app: notification-api
template:
metadata:
labels:
app: notification-api
spec:
containers:
- name: notification-api
image: mcr.microsoft.com/dotnet/aspnet:10.0
ports:
- containerPort: 8080
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: SmtpSettings__Host
valueFrom:
configMapKeyRef:
name: notification-api-config
key: SMTP_HOST
- name: SmtpSettings__Port
valueFrom:
configMapKeyRef:
name: notification-api-config
key: SMTP_PORT
- name: Logging__LogLevel__Default
valueFrom:
configMapKeyRef:
name: notification-api-config
key: LOG_LEVEL
envFrom:
- configMapRef:
name: notification-api-config
volumeMounts:
- name: appsettings-volume
mountPath: /app/config
readOnly: true
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
volumes:
- name: appsettings-volume
configMap:
name: notification-api-appsettings

Apply it:

kubectl apply -f notification-api-deployment.yaml

Let's break down what this Deployment does with ConfigMaps:

Step 4: Understanding the Environment Variable Methods

There are two different environment-variable approaches used in the Deployment above:

Method A: Individual Keys with valueFrom

This approach picks specific keys from a ConfigMap and maps them to environment variable names you choose:

env:
- name: SmtpSettings__Host # The env var name your app sees
valueFrom:
configMapKeyRef:
name: notification-api-config # Which ConfigMap
key: SMTP_HOST # Which key inside it

This is useful when the ConfigMap key names do not match what your application expects. In .NET, double underscores (__) are interpreted as section separators in the configuration hierarchy. So SmtpSettings__Host translates to SmtpSettings:Host in your IConfiguration object.

Method B: All Keys at Once with envFrom

This approach injects every key from the ConfigMap as an environment variable:

envFrom:
- configMapRef:
name: notification-api-config

Each key in the ConfigMap becomes an environment variable with the same name. In our example, the container will have SMTP_HOST, SMTP_PORT, RETRY_COUNT, LOG_LEVEL, and ALLOWED_ORIGINS all available as environment variables. This is the quickest approach when you control the key naming in the ConfigMap.

Step 5: Understanding the Volume Mount Method

The volume mount projects the ConfigMap as files inside the container's filesystem:

volumeMounts:
- name: appsettings-volume
mountPath: /app/config # Directory where files will appear
readOnly: true

volumes:
- name: appsettings-volume
configMap:
name: notification-api-appsettings

After the Pod starts, the container will see the following file:

/app/config/appsettings.Production.json

The file name comes from the key in the ConfigMap, and the file content comes from its value. If the ConfigMap had three keys, there would be three files in /app/config/.

You can verify this by exec-ing into a running Pod:

kubectl exec -it deploy/notification-api -- ls /app/config/

And view the file content:

kubectl exec -it deploy/notification-api -- cat /app/config/appsettings.Production.json

Step 6: Verify Environment Variables

To confirm that environment variables from the ConfigMap were injected correctly, run:

kubectl exec -it deploy/notification-api -- printenv | grep -E "SMTP|LOG|RETRY|ALLOWED"

You should see output similar to:

SMTP_HOST=mail.internal.local
SMTP_PORT=587
LOG_LEVEL=Information
RETRY_COUNT=3
ALLOWED_ORIGINS=https://portal.example.com,https://admin.example.com

Step 7: Using Command-Line Arguments from ConfigMap Values

The third consumption method injects ConfigMap values into a container's startup command. Kubernetes substitutes $(VAR_NAME) references in the command or args fields with the corresponding environment variable values. Here is a standalone example:

apiVersion: v1
kind: Pod
metadata:
name: config-cmd-demo
spec:
containers:
- name: demo
image: busybox:1.37
command: ["sh", "-c", "echo Host=$(SMTP_HOST) Port=$(SMTP_PORT) && sleep 3600"]
env:
- name: SMTP_HOST
valueFrom:
configMapKeyRef:
name: notification-api-config
key: SMTP_HOST
- name: SMTP_PORT
valueFrom:
configMapKeyRef:
name: notification-api-config
key: SMTP_PORT
resources:
requests:
cpu: "50m"
memory: "32Mi"
limits:
cpu: "100m"
memory: "64Mi"
restartPolicy: Never

When this Pod starts, Kubernetes replaces $(SMTP_HOST) and $(SMTP_PORT) with their actual values from the ConfigMap. The container's command becomes:

sh -c "echo Host=mail.internal.local Port=587 && sleep 3600"

This technique is especially useful for tools that require configuration via command-line flags rather than environment variables.

Step 8: Updating a ConfigMap

To change the log level from Information to Debug, edit the ConfigMap:

kubectl edit configmap notification-api-config

Change the value of the LOG_LEVEL key and save. Alternatively, you can replace the entire ConfigMap by re-applying an updated YAML file:

kubectl apply -f notification-api-config.yaml

Important behavior differences after an update:

  1. Volume-mounted files are updated automatically by the kubelet. There is a short delay (typically 30–60 seconds) before the new content appears. Your application will see the updated files without a restart — as long as it re-reads the file.
  2. Environment variables are not updated automatically. They are set once when the Pod starts. To pick up changes passed as environment variables, you must restart the Pods:
kubectl rollout restart deployment/notification-api

Step 9: Clean Up

When you are finished experimenting, remove all the resources:

kubectl delete deployment notification-api
kubectl delete configmap notification-api-config
kubectl delete configmap notification-api-appsettings

Or, if all manifests are in the same directory, you can delete them in one command:

kubectl delete -f notification-api-deployment.yaml
kubectl delete -f notification-api-config.yaml
kubectl delete -f notification-api-appsettings.yaml

Summary

ConfigMaps let you decouple configuration from container images so you can follow the build once, run anywhere principle. Here is what we covered:

  1. A ConfigMap stores non-sensitive configuration as string key-value pairs inside a Kubernetes Namespace.
  2. You can create ConfigMaps imperatively with kubectl create configmap (from literals, files, or directories) or declaratively from a YAML manifest.
  3. There are three ways to consume a ConfigMap in a Pod:
  4. Environment variables — using valueFrom.configMapKeyRef for individual keys or envFrom for all keys at once.
  5. Volume mounts — mounting the ConfigMap as a directory where each key becomes a file.
  6. Command-line arguments — referencing ConfigMap-sourced environment variables with $(VAR_NAME) syntax in command or args.
  7. Volume-mounted ConfigMaps are updated automatically (with a short delay). Environment-variable ConfigMaps require a Pod restart to pick up changes.
  8. ConfigMaps are for non-sensitive data only. For passwords, API keys, and certificates, use Secrets instead.


Share this lesson: