Kubernetes ConfigMaps and Secrets

The two Kubernetes primitives that decouple configuration from container images. ConfigMaps hold non-sensitive settings; Secrets hold sensitive credentials. Both are namespace-scoped, stored in etcd, and consumed by Pods as environment variables or mounted volumes. A core Architecture & Workloads topic on the CKA exam. Synthesized from CKA Day 19 — Kubernetes ConfigMap and Secret Explained.

Why Decouple Configuration?

Baking environment-specific values (database URLs, API keys, feature flags) into container images violates the twelve-factor app methodology. It forces you to build a new image for every environment and makes rollbacks painful.

Kubernetes solves this with ConfigMaps and Secrets:

BenefitHow It Works
Environment paritySame image runs in dev, staging, and prod — only the referenced ConfigMap/Secret changes
Dynamic updatesVolume-mounted ConfigMaps/Secrets update in-place without Pod restarts
Secret isolationSensitive data lives in Secrets with tighter RBAC and tmpfs mounting
Centralized configOne ConfigMap can feed dozens of Pods via label selectors

ConfigMap

What Is a ConfigMap?

A ConfigMap is an API object that stores plain-text configuration data as key-value pairs. It is not encrypted and should never hold passwords, tokens, or keys.

Creation Methods

ConfigMaps support four creation patterns — know them all for the exam:

1. From Literal Values (Imperative)

kubectl create configmap app-config \
  --from-literal=db_host=postgres.default.svc.cluster.local \
  --from-literal=db_port=5432

Best for: Exam speed, one-off debugging.

2. From a File (Imperative)

kubectl create configmap nginx-config --from-file=nginx.conf

The file name becomes the key; the file contents become the value. Multiple --from-file flags can bundle several files.

3. From a Directory (Imperative)

kubectl create configmap app-config --from-file=config/

Every file in the directory becomes a key-value pair in the ConfigMap.

4. Declarative (YAML)

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  db_host: "postgres.default.svc.cluster.local"
  db_port: "5432"
  feature_flag_caching: "true"

Best for: GitOps, version control, reproducible environments.

Exam Trap: The data field only accepts UTF-8 strings. If you need binary data (e.g., a .p12 certificate), use binaryData instead. Source: CKA Day 19


Secret

What Is a Secret?

A Secret is structurally identical to a ConfigMap but designed for sensitive data. Key differences:

AspectConfigMapSecret
Data fielddata (plain UTF-8 strings)data (base64-encoded bytes)
Size limit1 MiB1 MiB
Default volume mountRegular filesystemIn-memory tmpfs (never touches node disk)
RBAC sensitivityLowHigh — restrict get/list on Secrets
Etcd storagePlain textBase64-encoded (not encrypted by default)

Critical Warning: Secrets are base64-encoded, not encrypted. Anyone who can kubectl get secret can decode the values. For production, enable encryption at rest via EncryptionConfiguration. Source: CKA Day 19

Built-in Secret Types

Kubernetes recognizes several Secret types. The type is metadata, not enforcement — but some controllers behave differently based on it.

TypePurpose
OpaqueGeneric user-defined secrets (default)
kubernetes.io/service-account-tokenAuto-generated bearer token for ServiceAccount authentication
kubernetes.io/dockercfgLegacy Docker registry authentication
kubernetes.io/dockerconfigjsonModern Docker registry authentication (.docker/config.json format)
kubernetes.io/basic-authUsername/password pairs for HTTP basic auth
kubernetes.io/ssh-authSSH private keys
kubernetes.io/tlsTLS certificate and private key pairs
bootstrap.kubernetes.io/tokenkubeadm node bootstrap tokens

Creating Secrets

Imperative (Auto Base64 Encodes)

# From literal values — kubectl encodes automatically
kubectl create secret generic db-secret \
  --from-literal=password=supersecret123 \
  --from-literal=username=admin
 
# From files
kubectl create secret tls tls-secret \
  --cert=tls.crt --key=tls.key
 
# Docker registry login
kubectl create secret docker-registry regcred \
  --docker-server=docker.io \
  --docker-username=myuser \
  --docker-password=mypass

Exam Tip: When using kubectl create secret generic --from-literal, you do not need to base64-encode manually. The CLI handles it. Source: CKA Day 19

Declarative (Manual Base64)

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  password: c3VwZXJzZWNyZXQxMjM=   # echo -n "supersecret123" | base64
  username: YWRtaW4=               # echo -n "admin" | base64
echo -n "supersecret123" | base64   # Encode for YAML
echo "c3VwZXJzZWNyZXQxMjM=" | base64 -d  # Decode to verify

Consuming ConfigMaps and Secrets in Pods

Method 1: Environment Variables

Reference a specific key as an environment variable:

spec:
  containers:
  - name: app
    image: myapp:v1
    env:
    - name: DATABASE_HOST
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: db_host
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: password

Inject all key-value pairs at once:

spec:
  containers:
  - name: app
    image: myapp:v1
    envFrom:
    - configMapRef:
        name: app-config
    - secretRef:
        name: db-secret

Caution: envFrom imports every key as an env var. Key names must be valid environment variable names (no hyphens, must start with a letter or underscore). Source: CKA Day 11

Method 2: Volume Mounts

Project ConfigMap or Secret keys as files inside the container:

spec:
  containers:
  - name: app
    image: nginx:alpine
    volumeMounts:
    - name: config-vol
      mountPath: /etc/nginx/conf.d
    - name: secret-vol
      mountPath: /etc/nginx/ssl
      readOnly: true
  volumes:
  - name: config-vol
    configMap:
      name: nginx-config
  - name: secret-vol
    secret:
      secretName: tls-secret

Result inside the container:

/etc/nginx/conf.d/
  ├── default.conf    # Key from nginx-config ConfigMap
/etc/nginx/ssl/
  ├── tls.crt         # Key from tls-secret Secret
  └── tls.key         # Key from tls-secret Secret

Live Updates with Volume Mounts

ObjectVolume Mount Updates?Env Var Updates?
ConfigMap✅ Yes — kubelet re-syncs files every ~60s❌ No — Pod must restart
Secret✅ Yes — kubelet re-syncs files every ~60s❌ No — Pod must restart

When mounted as a volume, the kubelet watches the referenced ConfigMap/Secret and updates the files in the container without restarting the Pod. The application must detect file changes itself (e.g., via inotify or a configuration reload mechanism).

Production Pattern: Use a sidecar or fsnotify in your app to reload configuration when mounted files change. Sidecar Pattern


ImagePullSecrets

A special Secret type (kubernetes.io/dockerconfigjson) used to authenticate with private container registries. Unlike other Secrets, it is referenced at the Pod spec level, not inside containers:

apiVersion: v1
kind: Pod
metadata:
  name: private-app
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - name: app
    image: myregistry.io/private/app:v1

Without imagePullSecrets, kubelet cannot pull the image and the Pod enters ImagePullBackOff.

Create the Secret:

kubectl create secret docker-registry regcred \
  --docker-server=myregistry.io \
  --docker-username=myuser \
  --docker-password=mypass

Security Best Practices

PracticeRationale
Enable encryption at restBy default, Secrets are base64 in etcd. Use EncryptionConfiguration to encrypt with AES-GCM or KMS.
Restrict RBAC on SecretsOnly grant get/list on Secrets to controllers and specific service accounts, not broad developer roles.
Prefer volume mounts over env vars for SecretsEnv vars leak in kubectl describe pod, docker inspect, and /proc/<pid>/environ. Mounted files in tmpfs are more isolated.
Rotate Secrets regularlyUpdate Secret objects and trigger rolling updates. Use external secret managers (Vault, AWS Secrets Manager) for heavy rotation.
Don’t commit Secrets to GitUse Sealed Secrets, External Secrets Operator, or CI/CD secret injection. Never store raw Secret YAML in version control.
Use dedicated namespacesIsolate application Secrets from system Secrets (kube-system).

Troubleshooting Matrix

SymptomCauseFix
CreateContainerConfigErrorConfigMap or Secret referenced but does not existCreate the object first; check namespace
Invalid value / key not foundTypo in configMapKeyRef.key or secretKeyRef.keyVerify key exists with kubectl get configmap -o yaml
ImagePullBackOffMissing imagePullSecrets for private registryCreate docker-registry Secret and reference in Pod spec
Secret value is base64 garbage in YAMLForgot to encode; or double-encodedUse echo -n "value" | base64 for YAML; use CLI for auto-encoding
ConfigMap too largeExceeds 1 MiB etcd object limitSplit into multiple ConfigMaps; use volumes for large files
ConfigMap/Secret changes not reflectedUsing env vars instead of volume mountsSwitch to volumeMount + volume pattern for live reload

CKA Speed Patterns

# Create ConfigMap from literals (exam speed)
kubectl create configmap app-config --from-literal=key=value
 
# Create ConfigMap from file
kubectl create configmap nginx-config --from-file=nginx.conf
 
# Create Secret from literals (auto base64)
kubectl create secret generic db-secret --from-literal=password=secret123
 
# View decoded Secret value
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
 
# Inject all ConfigMap keys as env vars
kubectl run debug --image=busybox --env-from=configMapRef:name=app-config --restart=Never

YAML Memory: For the exam, memorize the valueFrom block structure:

valueFrom:
  configMapKeyRef:
    name: app-config
    key: db_host

and the envFrom shortcut:

envFrom:
- configMapRef:
    name: app-config


Tags: kubernetes configmap secret configuration security cka devops encryption-at-rest imagepullsecrets # twelve-factor-app