All writing
Updated 3 min readNaren

ConfigMaps and Secrets

Keep configuration out of your images with ConfigMaps, handle passwords and keys with Secrets, and know what base64 does and does not protect.

The last post covered cluster maintenance: upgrades and etcd backups. This one turns to applications, and how they get their configuration and sensitive data.

Kubernetes has two resources for this. ConfigMaps hold configuration that isn't confidential. Secrets hold sensitive information such as passwords and API keys. Both help keep applications portable and secure.

ConfigMaps

Say your application needs different settings in development, staging and production, such as API endpoints or logging levels. If you hardcode those values into the application code or the container image, the application is inflexible, and every change to an environment means rebuilding the image.

A ConfigMap is a Kubernetes API object that stores non-confidential data as key-value pairs. It separates configuration from your application code and container images, which makes your applications more portable and easier to manage.

Your container images stay generic, because the configuration is injected when the container runs. You can change the configuration without rebuilding the image. And you can manage the configuration of many Pods from one place.

Creating ConfigMaps

You can create a ConfigMap from literal values, from files or from an entire directory.

From literal values

For simple key-value pairs:

kubectl create configmap my-app-config --from-literal=log_level=INFO --from-literal=feature_flag=true

From files

For configuration files, first create the file, for example config.properties:

# config.properties
app.name=MyApplication
app.version=1.0

Then create the ConfigMap from it:

kubectl create configmap my-file-config --from-file=config.properties

To store the file under a different key name in the ConfigMap, put the key name before the file:

kubectl create configmap my-file-config-renamed --from-file=app_settings=config.properties

From a directory

If you have a directory full of configuration files, you can create one ConfigMap from all of them:

# Assuming you have a directory named 'config-files' with multiple files
# mkdir config-files
# echo "db.host=localhost" > config-files/db.conf
# echo "api.url=http://api.example.com" > config-files/api.conf
kubectl create configmap my-dir-config --from-file=config-files/

Using ConfigMaps in Pods

A Pod can consume a ConfigMap in several ways:

Pod ConfigMap Consumption as Env Var as Volume Mount Container ConfigMap

As environment variables

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod-env
spec:
  containers:
  - name: my-container
    image: busybox:1.36
    command: ["/bin/sh", "-c", "echo Log Level: $LOG_LEVEL && echo Feature Flag: $FEATURE_FLAG && sleep 3600"]
    env:
    - name: LOG_LEVEL
      valueFrom:
        configMapKeyRef:
          name: my-app-config # Name of the ConfigMap
          key: log_level     # Key within the ConfigMap
    - name: FEATURE_FLAG
      valueFrom:
        configMapKeyRef:
          name: my-app-config
          key: feature_flag

As files in a volume

This is often the better choice for configuration files, because your application can read them from an ordinary file path:

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod-volume
spec:
  containers:
  - name: my-container
    image: busybox:1.36
    command: ["/bin/sh", "-c", "cat /etc/config/config.properties && sleep 3600"]
    volumeMounts:
    - name: config-volume
      mountPath: "/etc/config" # Directory where config files will be mounted
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: my-file-config # Name of the ConfigMap

Each key in the ConfigMap appears as a separate file in the mounted directory, here /etc/config/config.properties.

Secrets

ConfigMaps are fine for general configuration, but not for sensitive information. Passwords, API keys, database credentials and other confidential data belong in Secrets.

Secrets are a safer way to handle sensitive data than embedding it in code or in a ConfigMap. They keep it separate from your application code and images, and Kubernetes has built-in ways to inject them into Pods.

Base64 is not encryption

The data in a Kubernetes Secret is stored as base64-encoded strings, and base64 encoding is not encryption. It only turns binary data into ASCII text, so the data can be sent and stored safely by text-based systems. Anyone who can read the encoded Secret can decode it.

Protecting Secrets properly takes more: encryption at rest for etcd, RBAC and external secret management.

Creating Secrets

With kubectl create secret

From literal values:

kubectl create secret generic my-db-secret --from-literal=username=admin --from-literal=password=sUp3rS3cr3tP@ssw0rd

Values typed on the command line end up in your shell history, which is a security risk.

Creating the Secret from files is the better way to use kubectl create. First write the values to files:

echo -n 'admin' > username.txt
echo -n 'sUp3rS3cr3tP@ssw0rd' > password.txt

Then create the Secret. The username= and password= prefixes set the key names, so the Pods below find the keys they expect:

kubectl create secret generic my-db-secret --from-file=username=username.txt --from-file=password=password.txt

With a YAML manifest

For declarative management, define the Secret in YAML. Values under data must be base64 encoded:

apiVersion: v1
kind: Secret
metadata:
  name: my-yaml-secret
type: Opaque # Default type for general-purpose secrets
data:
  username: YWRtaW4= # echo -n 'admin' | base64
  password: c1VwM3JTM2NyM3RQQHNzdzByZA== # echo -n 'sUp3rS3cr3tP@ssw0rd' | base64

For readability, use stringData instead, and Kubernetes base64-encodes the values for you:

apiVersion: v1
kind: Secret
metadata:
  name: my-yaml-secret-stringdata
type: Opaque
stringData:
  username: admin
  password: sUp3rS3cr3tP@ssw0rd

Apply it with kubectl apply -f my-secret.yaml.

Using Secrets in Pods

Pod Secret Consumption as Env Var as Volume Mount Container Secret

As environment variables

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod-secret-env
spec:
  containers:
  - name: my-container
    image: busybox:1.36
    command: ["/bin/sh", "-c", "echo DB Username: $DB_USERNAME && echo DB Password: $DB_PASSWORD && sleep 3600"]
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: my-db-secret # Name of the Secret
          key: username     # Key within the Secret
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: my-db-secret
          key: password

Environment variables are easy to expose by accident. Mounting the Secret as files is more secure.

As files in a volume

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod-secret-volume
spec:
  containers:
  - name: my-container
    image: busybox:1.36
    command: ["/bin/sh", "-c", "echo DB Username: $(cat /etc/db-credentials/username) && echo DB Password: $(cat /etc/db-credentials/password) && sleep 3600"]
    volumeMounts:
    - name: db-credentials-volume
      mountPath: "/etc/db-credentials" # Directory where secret files will be mounted
      readOnly: true
  volumes:
  - name: db-credentials-volume
    secret:
      secretName: my-db-secret # Name of the Secret
      defaultMode: 0400 # Optional: set file permissions

Each key in the Secret becomes a file in the mounted directory.

For pulling images from a private registry

If you use a private container registry, Kubernetes needs credentials to pull images from it. Store them in a docker-registry Secret:

kubectl create secret docker-registry my-registry-secret \
  --docker-server=private-registry.example.com \
  --docker-username=your-username \
  --docker-password=your-password \
  --docker-email=your-email@example.com

Then reference it from the Pod's imagePullSecrets:

apiVersion: v1
kind: Pod
metadata:
  name: my-private-image-pod
spec:
  containers:
  - name: my-container
    image: private-registry.example.com/my-app:latest
  imagePullSecrets:
  - name: my-registry-secret # Name of the Secret containing registry credentials

Good practices for Secrets

Configure your cluster to encrypt etcd data at rest, and use RBAC to restrict who can read or change Secrets.

Rotate Secrets regularly, so a leaked one is useful for less time.

For high-security needs, integrate an external secret manager such as HashiCorp Vault. With one in place, the Secrets Store CSI Driver fetches the secret and mounts it into the Pod as a volume:

Kubernetes Cluster requests secret authenticates with provides secret mounts secret as volume Pod Secrets Store CSI Driver External Secret Manager (e.g., Vault)

Prefer mounting Secrets as files over environment variables, and use stringData to keep Secret manifests readable.

For GitOps, encrypt Secret manifests with Sealed Secrets or SOPS before you commit them to Git.

The next post continues with application development: probes and health checks, which keep applications running and ready for traffic. Liveness, readiness and startup probes.