Kubernetes

Kubernetes ConfigMaps and Secrets: A Practical Guide

How to externalize configuration and sensitive values from your container images using ConfigMaps and Secrets — and why Secrets alone aren't actually encryption.

Marcus LeeJune 8, 20263 min read
Share:

Baking configuration and credentials directly into a container image means rebuilding the image every time a value changes, and it means secrets end up in your image layers. ConfigMaps and Secrets exist to separate configuration from the image itself.

ConfigMaps — for non-sensitive configuration

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
  FEATURE_FLAG_NEW_UI: "true"

Secrets — for sensitive values

yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  DATABASE_PASSWORD: cG9zdGdyZXNfcGFzc3dvcmQ=  # base64-encoded
bash
# Easier than hand-encoding base64 yourself
kubectl create secret generic app-secrets \
  --from-literal=DATABASE_PASSWORD=postgres_password

Using either as environment variables

yaml
spec:
  containers:
    - name: app
      envFrom:
        - configMapRef:
            name: app-config
        - secretRef:
            name: app-secrets

envFrom injects every key from the ConfigMap/Secret as an environment variable in one shot — useful when you have many values and don't want to list each individually.

Or mounted as files

yaml
spec:
  containers:
    - name: app
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: app-config

Each key becomes a file in the mounted directory, with the value as its contents — the right choice for applications that expect configuration as files (like a .env file or an nginx config) rather than environment variables, or for large values environment variables aren't well-suited to.

The important caveat: Secrets aren't encrypted by default

kubectl get secret app-secrets -o yaml shows the value base64-encoded, not encrypted — base64 is an encoding, trivially reversible (echo "..." | base64 -d), not a security measure. Anyone with kubectl access to read Secret objects in that namespace can decode them instantly.

Real protection requires layering on top of the base Secret object:

  • Encryption at rest — configuring the cluster's API server with an encryption provider so Secrets are actually encrypted in etcd, not just base64-encoded.
  • RBAC — restricting who can read Secret objects via Kubernetes' role-based access control, so "base64 isn't encryption" matters less if far fewer people/services can read the object in the first place.
  • External secret managers — tools like AWS Secrets Manager, HashiCorp Vault, or Google Secret Manager integrated via an operator (e.g., External Secrets Operator), so the actual sensitive value lives in a purpose-built secret store, and Kubernetes only holds a reference or a short-lived synced copy.

A reasonable default approach

For low-sensitivity config: ConfigMaps, without a second thought. For genuinely sensitive values (database credentials, API keys, tokens): native Secrets are fine for getting started, but as soon as multiple people have cluster access or the values are genuinely high-stakes, layer on RBAC restrictions and consider an external secret manager rather than treating Secrets as sufficient protection on their own.

Advertisement

Frequently Asked Questions

Marcus Lee
Marcus Lee

Cloud & DevOps Engineer

Marcus covers Kubernetes, cloud infrastructure, and CI/CD. He's spent a decade running production systems at scale.

Related Articles