Skip to main content

Kubernetes Ep 7: ConfigMaps, Secrets & Environment Variables

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
kubernetes - This article is part of a series.
Part 7: This Article
The Twelve-Factor App methodology mandates strict separation of application code from configuration settings. ConfigMaps store non-sensitive configuration data (URLs, log levels), while Secrets store sensitive values (passwords, API tokens, TLS keys).

TL;DR (Quick Summary)
#

  • ConfigMaps: Store plain-text key-value pairs or whole configuration files (app.conf).
  • Secrets: Store base64-encoded sensitive values. Types include Opaque (generic), kubernetes.io/dockerconfigjson (image pull secrets), and kubernetes.io/tls.
  • Injection Methods:
    1. Environment Variables (env / envFrom).
    2. Volume Mounts (mounted as read-only files inside container filesystems).
  • Security Caution: Base64 encoding is NOT encryption. Use RBAC restrictions, etcd encryption-at-rest, or HashiCorp Vault / External Secrets Operator in production.

1. ConfigMaps Deep Dive
#


graph LR
    CM["ConfigMap: app-config
LOG_LEVEL=debug
DB_HOST=postgres.default"] -->|1. Inject as Env Vars| Pod1["Pod A (env: DB_HOST)"] CM -->|2. Mount as Volume /etc/config| Pod2["Pod B (File: /etc/config/app.json)"]

Creating a ConfigMap (Declarative)
#

Create configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  LOG_LEVEL: "info"
  APP_FEATURE_FLAG: "true"
  nginx.conf: |
    server {
        listen 80;
        location / {
            return 200 "Configured via ConfigMap Volume!";
        }
    }

Apply manifest:

kubectl apply -f configmap.yaml

2. Secrets Deep Dive
#

Creating a Secret (Imperative & Base64 Encoding)
#

Generate base64 strings:

echo -n "super-secret-password" | base64
# Output: c3VwZXItc2VjcmV0LXBhc3N3b3Jk

Create secret.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: default
type: Opaque
data:
  DB_USER: cG9zdGdyZXM=               # base64 for 'postgres'
  DB_PASSWORD: c3VwZXItc2VjcmV0LXBhc3N3b3Jk # base64 for 'super-secret-password'

Apply manifest:

kubectl apply -f secret.yaml

3. Consuming ConfigMaps & Secrets in Pods
#

Method A: Ingesting Key-Value Pairs as Environment Variables
#

Create pod-env.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: app-env-pod
spec:
  containers:
  - name: app-container
    image: alpine
    command: ["sh", "-c", "env && sleep 3600"]
    env:
    # Single field from ConfigMap
    - name: APPLICATION_LOG_LEVEL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: LOG_LEVEL
    # Single field from Secret
    - name: DATABASE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: DB_PASSWORD

Apply and verify injected environment variables:

kubectl apply -f pod-env.yaml
kubectl exec app-env-pod -- env | grep -E "LOG_LEVEL|DATABASE_PASSWORD"

Expected Terminal Output:

APPLICATION_LOG_LEVEL=info
DATABASE_PASSWORD=super-secret-password

Method B: Mounting Configuration Files as Volumes
#

When mounting a ConfigMap as a Volume, files inside the mounted directory automatically update when the ConfigMap is modified—without requiring a container restart!

Create pod-volume-config.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-configmap-volume-pod
spec:
  containers:
  - name: web-server
    image: nginx:alpine
    volumeMounts:
    - name: config-volume
      mountPath: /etc/nginx/conf.d/default.conf
      subPath: nginx.conf
  volumes:
  - name: config-volume
    configMap:
      name: app-config

4. Production Security Best Practices
#

Warning

By default, etcd stores Secrets as plain base64 strings. Follow these production rules:

  1. Enable Encryption at Rest: Configure kube-apiserver with --encryption-provider-config to encrypt etcd secrets using AES-CBC or KMS.
  2. Restrict RBAC Access: Restrict get, list, and watch permissions on Secret resources to authorized service accounts only.
  3. Use External Secrets Operator (ESO): Synchronize secrets dynamically from enterprise secret managers (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault) directly into K8s Secrets.

5. Summary & Next Steps
#

ConfigMaps and Secrets handle runtime configuration. However, container filesystems are ephemeral—when a Pod restarts, all files created inside the container are wiped out.

In Episode 08: Persistent Volumes, PVCs & StorageClasses, we will master stateful storage abstractions to persist data across Pod restarts and node failures!

kubernetes - This article is part of a series.
Part 7: This Article