ConfigMaps and Secrets in Kubernetes
Disclaimer: This post was originally written for my company’s blog . I’ve translated it and added a little personal touch for this space.
One of Kubernetes’ key features is the ability to manage configuration data and sensitive information within our cluster both secure and surprisingly easy. Keeping our configuration and secrets organized is one of those small things that saves us headaches later. In this post, we’ll take a closer look at the two Kubernetes objects that make this happen: ConfigMaps and Secrets.
ConfigMaps
ConfigMaps let us store non-sensitive configuration data separately from our applications. Think of them as a way to keep our configuration files, environment variables, and other settings organized and easy to update — without touching our app itself.
Creating a ConfigMap
There are a few ways to create a ConfigMap: using YAML files, the kubectl command line, or directly via the Kubernetes API. Here is a simple YAML example:
apiVersion: v1
kind: ConfigMap
metadata:
name: example-configmap
data:
DATABASE_HOST: "example-host"
DATABASE_USER: "example-user"
We can apply it to our cluster with:
kubectl apply -f example-configmap.yaml
Now our configuration lives in our cluster and is ready for our pods to use. We can inject these values now in a few ways - but in practice, most people use either environment variables or mounted volumes. Let’s hence take a closer look at both.
Using a ConfigMap as Environment Variables
Environment variables are great for passing small sets of simple values to our applications. Here’s how to inject the whole ConfigMap into a pod:
apiVersion: v1
kind: Pod
metadata:
name: example-app
spec:
containers:
- name: example-container
image: example-image
envFrom:
configMapRef:
name: example-configmap
If we only need specific values, we can inject them individually:
apiVersion: v1
kind: Pod
metadata:
name: example-app
spec:
containers:
- name: example-container
image: example-image
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: example-configmap
key: DATABASE_HOST
Mounting a ConfigMap as a Volume
For larger or more complex configuration data, mounting ConfigMaps as volumes is usually more convenient than environment variables - and also straightforward:
apiVersion: v1
kind: Pod
metadata:
name: example-app
spec:
containers:
- name: example-container
image: example-image
volumeMounts:
- name: config
mountPath: etc/example-app-config
readOnly: true
volumes:
- name: config
configMap:
name: example-configmap
Secrets
Secrets are a lot like ConfigMaps, but they’re built specifically for sensitive information such as passwords, tokens, or keys. They’re encoded and have strict access controls, which makes them a safer way to handle confidential data in our cluster.
Creating a Secret
Like with ConfigMaps, we can create Secrets via YAML files, kubectl, or the Kubernetes API. Here’s a simple YAML example:
apiVersion: v1
kind: Secret
metadata:
name: example-secret
type: Opaque
data:
DATABASE_PASSWORD: ZXhhbXBsZS1wYXNzd29yZA== # base64 encoding of 'example-password'
To encode a value in base64, we can run:
echo -n 'example-password' | base64
Then we deploy the Secret to our cluster:
kubectl apply -f example-secret.yaml
Important: Kubernetes only encodes Secrets in base64 — it doesn’t actually encrypt them. That means if we store Secrets in shared repositories, anyone can decode them easily - not secure at all. Tools like SOPS can provide real encryption and protect our sensitive data. Also, inside Kubernetes, Secrets are stored unencrypted, so we should always restrict cluster access carefully.
Using a Secret
Just like ConfigMaps, Secrets can be injected into our pods either as environment variables or as files on the filesystem.
Using a Secret as an environment variable:
apiVersion: v1
kind: Pod
metadata:
name: example-app
spec:
containers:
- name: example-container
image: example-image
env:
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: example-secret
key: DATABASE_PASSWORD
Mounting a Secret as a volume:
apiVersion: v1
kind: Pod
metadata:
name: example-app
spec:
containers:
- name: example-container
image: example-image
volumeMounts:
- name: secret-volume
mountPath: etc/secret-volume
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: example-secret
Immutability of ConfigMaps & Secrets
So far, all the configurations we’ve looked at can be updated after creation. Sometimes that’s exactly what we want, but other times, unintended changes can break things or cause inconsistencies in our cluster. That’s where immutability comes in.
We can make a ConfigMap or Secret immutable by adding the immutable: true field in the YAML:
apiVersion: v1
kind: ConfigMap
metadata:
name: example-configmap
data:
DATABASE_HOST: "example-host"
DATABASE_USER: "example-user"
immutable: true
Or for Secrets:
apiVersion: v1
kind: Secret
metadata:
name: example-secret
type: Opaque
data:
DATABASE_PASSWORD: ZXhhbXBsZS1wYXNzd29yZA==
immutable: true
Once deployed in our cluster, we can’t change them — great for stability, but it also means we need to think ahead. Because if we want to update values, we have to delete and recreate the object, and any pods using it will need a refresh too. Quite some work. 🤪 So, immutability is super handy, but we should consider carefully when and where to use it during development.
