13 - ConfigMaps & Secrets

Why ConfigMaps and Secrets?

Separate configuration from code so you can:

  • Use the same image across environments (dev/staging/prod)
  • Change config without rebuilding images
  • Manage sensitive data securely

ConfigMaps

Store non-sensitive configuration data as key-value pairs.

Creating ConfigMaps

bash
# From literal values kubectl create configmap app-config \ --from-literal=DATABASE_HOST=db.example.com \ --from-literal=DATABASE_PORT=5432 \ --from-literal=LOG_LEVEL=info # From a file kubectl create configmap nginx-config \ --from-file=nginx.conf # From a directory (each file becomes a key) kubectl create configmap app-configs \ --from-file=./config/ # From .env file kubectl create configmap app-env \ --from-env-file=.env

ConfigMap YAML

yaml
apiVersion: v1 kind: ConfigMap metadata: name: app-config namespace: default data: # Simple key-value pairs DATABASE_HOST: "db.example.com" DATABASE_PORT: "5432" LOG_LEVEL: "info" # Multi-line config file app.properties: | server.port=8080 server.host=0.0.0.0 cache.ttl=300 nginx.conf: | server { listen 80; server_name _; location / { proxy_pass http://api:8080; } }

Using ConfigMaps in Pods

yaml
apiVersion: v1 kind: Pod metadata: name: my-app spec: containers: - name: app image: myapp:v1 # Method 1: Individual environment variables env: - name: DB_HOST valueFrom: configMapKeyRef: name: app-config key: DATABASE_HOST - name: DB_PORT valueFrom: configMapKeyRef: name: app-config key: DATABASE_PORT # Method 2: All keys as environment variables envFrom: - configMapRef: name: app-config # All keys become env vars: # DATABASE_HOST=db.example.com # DATABASE_PORT=5432 # LOG_LEVEL=info # Method 3: Mount as files in a volume volumeMounts: - name: config-volume mountPath: /etc/app/config readOnly: true - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf # Mount single file, not directory volumes: - name: config-volume configMap: name: app-config # Each key becomes a file: # /etc/app/config/DATABASE_HOST (content: db.example.com) # /etc/app/config/DATABASE_PORT (content: 5432) - name: nginx-config configMap: name: app-config items: # Mount only specific keys - key: nginx.conf path: nginx.conf

Auto-Updating ConfigMaps

When mounted as a volume, ConfigMap changes are auto-propagated to pods (within ~1 minute). But:

  • Environment variables are NOT updated (requires pod restart)
  • subPath mounts are NOT updated
  • Apps must watch for file changes to pick up new config

Secrets

Store sensitive data (passwords, tokens, certificates). Base64-encoded (NOT encrypted by default).

Creating Secrets

bash
# From literal values kubectl create secret generic db-credentials \ --from-literal=username=admin \ --from-literal=password='S3cr3tP@ss!' # From files kubectl create secret generic tls-cert \ --from-file=tls.crt=./server.crt \ --from-file=tls.key=./server.key # TLS secret (special type) kubectl create secret tls my-tls \ --cert=server.crt \ --key=server.key # Docker registry credentials kubectl create secret docker-registry regcred \ --docker-server=ghcr.io \ --docker-username=myuser \ --docker-password=mytoken

Secret YAML

yaml
apiVersion: v1 kind: Secret metadata: name: db-credentials type: Opaque # Generic secret data: # Values MUST be base64 encoded username: YWRtaW4= # echo -n "admin" | base64 password: UzNjcjN0UEBzcyE= # echo -n "S3cr3tP@ss!" | base64 --- # Or use stringData for plain text (auto-encoded) apiVersion: v1 kind: Secret metadata: name: db-credentials type: Opaque stringData: username: admin password: "S3cr3tP@ss!"

Secret Types

TypeDescription
OpaqueGeneric (default)
kubernetes.io/tlsTLS cert/key pair
kubernetes.io/dockerconfigjsonDocker registry auth
kubernetes.io/basic-authUsername/password
kubernetes.io/ssh-authSSH private key
kubernetes.io/service-account-tokenService account token

Using Secrets in Pods

yaml
apiVersion: v1 kind: Pod metadata: name: my-app spec: containers: - name: app image: myapp:v1 # As environment variables env: - name: DB_USERNAME valueFrom: secretKeyRef: name: db-credentials key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password # As files volumeMounts: - name: secrets mountPath: /etc/secrets readOnly: true # Image pull secret (private registry) imagePullSecrets: - name: regcred volumes: - name: secrets secret: secretName: db-credentials defaultMode: 0400 # Read-only for owner # Files created: # /etc/secrets/username (content: admin) # /etc/secrets/password (content: S3cr3tP@ss!)

Security Warning: Secrets Are NOT Encrypted

By default, Kubernetes Secrets are:

  • Base64-encoded (NOT encryption)
  • Stored in plaintext in etcd
  • Visible to anyone with API access

Securing Secrets

yaml
# 1. Enable encryption at rest in etcd # /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: EncryptionConfiguration resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: <base64-encoded-32-byte-key> - identity: {} # Fallback to plaintext for reading old secrets
bash
# 2. Use external secret managers # - AWS Secrets Manager + External Secrets Operator # - HashiCorp Vault + Vault Secrets Operator # - Azure Key Vault + CSI Secret Store Driver # - GCP Secret Manager # 3. RBAC: restrict who can read secrets # See 18 - RBAC & Security # 4. Audit logging: track secret access

External Secrets Operator (Recommended)

yaml
# Syncs secrets from external providers to K8s Secrets apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: db-credentials spec: refreshInterval: 1h secretStoreRef: name: aws-secrets-manager kind: ClusterSecretStore target: name: db-credentials data: - secretKey: username remoteRef: key: production/db property: username - secretKey: password remoteRef: key: production/db property: password

Immutable ConfigMaps and Secrets

yaml
apiVersion: v1 kind: ConfigMap metadata: name: app-config-v2 immutable: true # Cannot be modified after creation data: LOG_LEVEL: "warn"

Benefits:

  • Protects against accidental changes
  • Improves cluster performance (no watch needed)
  • Must create new version and update pod references to change

kubectl Commands

bash
# ConfigMaps kubectl get configmaps kubectl describe configmap app-config kubectl get configmap app-config -o yaml kubectl edit configmap app-config kubectl delete configmap app-config # Secrets kubectl get secrets kubectl describe secret db-credentials # Shows keys but not values kubectl get secret db-credentials -o yaml # Shows base64 values kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d # Decode a secret value echo "UzNjcjN0UEBzcyE=" | base64 -d # S3cr3tP@ss!

FAANG Interview Angle

Common questions:

  1. "How do you manage configuration in Kubernetes?"
  2. "Are Kubernetes Secrets secure? How would you improve security?"
  3. "What's the difference between ConfigMaps and Secrets?"
  4. "How do you update configuration without restarting pods?"
  5. "How would you manage secrets across environments?"

Key answers:

  • ConfigMaps for non-sensitive config, Secrets for sensitive data; both decouple config from images
  • Secrets are base64 (not encrypted); improve with etcd encryption, RBAC, external secret managers
  • ConfigMaps: plaintext, no size warnings. Secrets: base64, special handling, 1MB limit
  • Volume-mounted ConfigMaps auto-update; env vars require restart
  • External Secrets Operator syncing from Vault/AWS Secrets Manager per environment

Official Links