Kubernetes
ConfigMap & Secret

Published: October 28, 2026 · 8 min read

Kubernetes ConfigMap and Secret Explained for Beginners

Your app needs a database address and a password. Right now both are inside the image.

A Kubernetes ConfigMap and a Secret take them out of it.

Here is the short version:

  • A ConfigMap holds plain settings. A Secret holds sensitive ones.
  • Both are just key and value pairs.
  • Both are namespaced — a Pod can only use ones in its own namespace.
  • A container reads them in two ways: environment variables, or files.
  • A Secret is base64 encoded, not encrypted.
  • Change a ConfigMap and nothing restarts on its own.

Kubernetes ConfigMap and Secret keep configuration out of the container image

The Problem

Put DB_PASSWORD = "s3cr3t-p@ss" in your Dockerfile and three things go wrong:

  • Change one value and you rebuild the whole image.
  • The password is now in git, forever, for everyone.
  • You need a different image for dev, qa and prod.

The image should say how your app runs. Not what it runs with.

ConfigMap vs Secret

Comparing a Kubernetes ConfigMap and a Secret side by side

ConfigMapSecret
Holdsplain settingssensitive values
Examplelog level, feature flag, API urlpassword, token, TLS key
Stored asplain textbase64 text
Safe in git?yesno
kubectl describe showsthe real value<hidden>

Same shape, same usage. Ask one question: would you read this out loud in a meeting? If no, it is a Secret.

Creating a ConfigMap

The quick way

terminal — create
# the quick way

kubectl create configmap hotel-config \
  --from-literal=LOG_LEVEL=debug \
  --from-literal=APP_MODE=production

# configmap/hotel-config created

kubectl get configmap
# NAME           DATA   AGE
# hotel-config   2      8s

kubectl get configmap hotel-config -o yaml
# data:
#   APP_MODE: production
#   LOG_LEVEL: debug

The YAML way

configmap.yaml
# configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: hotel-config
  namespace: default
data:
  LOG_LEVEL: "debug"
  APP_MODE: "production"
  MAX_BOOKINGS: "50"

Same four parts as every manifest: apiVersion, kind, metadata, and then data instead of spec.

⚠️

Every value under data must be a string. Write MAX_BOOKINGS: "50" with quotes. Plain 50 is a number and is rejected.

From a whole file

Your app may already read a config file. You do not have to split it into keys.

terminal — from-file
# a whole file becomes ONE key

cat app.properties
# log.level=debug
# max.bookings=50
# feature.newCheckout=true

kubectl create configmap hotel-file-config --from-file=app.properties
# configmap/hotel-file-config created

kubectl describe configmap hotel-file-config
# Data
# ====
# app.properties:
# ----
# log.level=debug
# max.bookings=50
# feature.newCheckout=true

# the KEY is the file name, the VALUE is the whole file
# mount it and you get /etc/app/app.properties back, unchanged

The file name becomes the key and the whole file becomes the value. Mount it as a volume and your app finds the exact same file it expects, at the path you chose.

A ConfigMap can hold at most 1 MiB. That is plenty for settings, and far too small for data. Anything bigger belongs in a volume.

Creating a Secret

terminal — create
# the quick way - kubectl does the base64 for you

kubectl create secret generic hotel-secret \
  --from-literal=DB_USER=admin \
  --from-literal=DB_PASSWORD=mypassword

# secret/hotel-secret created

kubectl get secret
# NAME           TYPE     DATA   AGE
# hotel-secret   Opaque   2      6s
secret.yaml
# secret.yaml  -  two ways to write the values

apiVersion: v1
kind: Secret
metadata:
  name: hotel-secret
type: Opaque

# option A - stringData, you write it plainly
stringData:
  DB_USER: "admin"
  DB_PASSWORD: "mypassword"

# option B - data, you must base64 it yourself
# data:
#   DB_USER: YWRtaW4=
#   DB_PASSWORD: bXlwYXNzd29yZA==

# echo -n 'mypassword' | base64   ->  bXlwYXNzd29yZA==

Two fields do the same job:

FieldYou writeKubernetes does
stringDatathe plain valueencodes it for you
datathe base64 valuestores it as given

Use stringData. It is easier to read and harder to get wrong.

A Secret Is Not Encrypted

This catches everyone, so be clear about it.

Why a Kubernetes Secret is base64 encoded and not encrypted

base64 is a format, not a lock. One command undoes it:

kubectl get secret hotel-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
mypassword
🚫

Never commit a Secret YAML to git. Base64 hides nothing. Anyone who can read the Secret can read the password.

Real protection takes three more things. Encryption at rest in the cluster. RBAC, so only some people may read Secrets. An external vault for anything serious.

Using Them in a Pod

Two ways a Kubernetes Pod reads a ConfigMap, as environment variables or as mounted files

1. As environment variables

deployment.yaml
# deployment.yaml  -  as environment variables

spec:
  containers:
    - name: hotel-app
      image: srinudev645/hotel-app:v1
      env:
        - name: LOG_LEVEL                 # the name inside the container
          valueFrom:
            configMapKeyRef:
              name: hotel-config          # which ConfigMap
              key: LOG_LEVEL              # which key in it

        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: hotel-secret          # which Secret
              key: DB_PASSWORD

Read it inside out. key picks a value. name picks the object. The outer name is what the variable is called in your container.

Two reference names, one for each object type:

ObjectReference
ConfigMapconfigMapKeyRef
SecretsecretKeyRef

Pulling in every key at once

deployment.yaml — envFrom
# pull in EVERY key at once

spec:
  containers:
    - name: hotel-app
      image: srinudev645/hotel-app:v1
      envFrom:
        - configMapRef:
            name: hotel-config            # all 3 keys become env vars
        - secretRef:
            name: hotel-secret            # both keys become env vars

# inside the container:
#   LOG_LEVEL=debug  APP_MODE=production  MAX_BOOKINGS=50
#   DB_USER=admin    DB_PASSWORD=mypassword

envFrom saves a lot of typing. Every key becomes a variable with the same name.

2. As files in a volume

deployment.yaml — volume
# deployment.yaml  -  as files on disk

spec:
  containers:
    - name: hotel-app
      image: srinudev645/hotel-app:v1
      volumeMounts:
        - name: config-volume
          mountPath: /etc/app             # the folder to create
          readOnly: true
  volumes:
    - name: config-volume
      configMap:
        name: hotel-config

# inside the container you now have:
#   /etc/app/LOG_LEVEL      -> debug
#   /etc/app/APP_MODE       -> production
#   /etc/app/MAX_BOOKINGS   -> 50
# one file per key, the file name IS the key

You get one file per key, and the file name is the key. Good for whole config files your app already knows how to read.

For a Secret, swap configMap: for secret: and name: for secretName:. Everything else is the same.

Which One Should You Use?

Environment variablesVolume files
Good fora few short valueswhole config files
Updates on change?no, needs a restartyes, after a short delay
Visible in kubectl describe podyesno
⚠️

Edit a ConfigMap and nothing restarts. Mounted files refresh on their own, but environment variables do not. For those you need kubectl rollout restart deployment <name>.

Checking It Worked

terminal — check
kubectl apply -f deployment.yaml

# did the values arrive?
kubectl exec -it hotel-app-7d4b9c6f85-2kq9x -- env | grep LOG_LEVEL
# LOG_LEVEL=debug

kubectl exec -it hotel-app-7d4b9c6f85-2kq9x -- cat /etc/app/LOG_LEVEL
# debug

# read a Secret back
kubectl get secret hotel-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d
# mypassword

Three questions, three commands: is the variable set, is the file there, and what is actually inside the Secret.

ConfigMap and Secret Commands

commands
# create
kubectl create configmap <name> --from-literal=KEY=value
kubectl create configmap <name> --from-file=app.properties
kubectl create secret generic <name> --from-literal=KEY=value
kubectl apply -f configmap.yaml

# look
kubectl get configmap
kubectl get secret
kubectl describe configmap <name>          # shows the values
kubectl describe secret <name>             # hides the values
kubectl get configmap <name> -o yaml

# read a secret value
kubectl get secret <name> -o jsonpath='{.data.KEY}' | base64 -d

# change and reload
kubectl edit configmap <name>
kubectl rollout restart deployment <name>  # needed for env vars

# delete
kubectl delete configmap <name>
kubectl delete secret <name>
CommandWhat it does
kubectl create configmap <name> --from-literal=K=vcreate from the command line
kubectl create configmap <name> --from-file=app.propertiescreate from a whole file
kubectl create secret generic <name> --from-literal=K=vcreate a Secret, encoded for you
kubectl get configmap / kubectl get secretlist them
kubectl describe configmap <name>show the values
kubectl get secret <name> -o jsonpath='{.data.K}' | base64 -dread one Secret value
kubectl rollout restart deployment <name>pick up new environment variables

What's Next

Config and passwords now live outside your image, and one image runs everywhere. The official ConfigMap page (opens in a new tab) and the Secret page (opens in a new tab) go deeper.

← Back to Resource Limits

Continue to Network Policy →


Frequently Asked Questions

What is a ConfigMap in Kubernetes?

A ConfigMap is a Kubernetes object that holds plain configuration as key and value pairs. Things like a log level, a feature flag or an API address. Your container image stays clean and the settings are handed to it when the Pod starts. One image can then run in dev, qa and prod with different values.

What is the difference between a ConfigMap and a Secret?

They have the same shape and are used the same way. A ConfigMap holds plain settings that are safe to read, such as a log level. A Secret holds sensitive values such as a password or a token, and Kubernetes stores those base64 encoded. kubectl describe shows ConfigMap values but hides Secret values. Only the intent is different.

Is a Kubernetes Secret encrypted?

No, not by default. A Secret is base64 encoded, and base64 is a format, not encryption. Anybody who can read the Secret can decode it with one command. To really protect it you turn on encryption at rest in the cluster. You also restrict who can read Secrets with RBAC, and keep Secret YAML out of git.

How do you use a ConfigMap inside a Pod?

Two ways. As environment variables, using valueFrom with configMapKeyRef for one key, or envFrom with configMapRef for every key at once. Or as files, by adding the ConfigMap as a volume and mounting it at a path. With a volume you get one file per key, and the file name is the key.

What is the difference between env vars and volume mounts for a ConfigMap?

Environment variables are read once when the container starts, so changing the ConfigMap does nothing until you restart the Pod. A mounted volume is different. Kubernetes updates the files on its own after a short delay, with no restart. Use env vars for a few short values and a volume for whole config files.

Do Pods restart automatically when a ConfigMap changes?

No. Kubernetes does not restart anything when you edit a ConfigMap or a Secret. If you used a volume mount, the files inside the container are refreshed on their own. Your application still has to notice and re-read them. If you used environment variables, nothing changes at all until you run kubectl rollout restart on the Deployment.

What is stringData in a Kubernetes Secret?

stringData lets you write the value plainly in your YAML. Kubernetes does the base64 encoding for you when it saves it. The data field is the other option, and there you must encode every value yourself first. Use stringData, because it is far easier to read and far harder to get wrong.

Can a ConfigMap and a Secret be used by Pods in another namespace?

No. Both are namespaced objects, so a Pod can only use a ConfigMap or Secret that lives in its own namespace. If two namespaces need the same values, you create the object in each one. There is no built in way to share a single copy across namespaces.