Learn Kubernetes in 10 DaysDay 7: ConfigMaps and Secrets

Day 7: ConfigMaps and Secrets

What You'll Learn Today

  • Managing configuration data with ConfigMaps
  • Managing sensitive data with Secrets
  • Using environment variables and volume mounts
  • Migrating from Docker Compose .env files

Why Separate Configuration?

In the Docker book, you managed settings with environment variables, docker-compose.yml environment sections, and .env files. Kubernetes handles this more systematically with ConfigMaps and Secrets.

flowchart TB
    subgraph Docker["Docker Compose"]
        ENV[".env file"]
        DENV["environment:\n  DB_HOST: db"]
    end
    subgraph K8s["Kubernetes"]
        CM["ConfigMap\n(general settings)"]
        SEC["Secret\n(sensitive data)"]
    end
    ENV -->|"migrate"| CM
    ENV -->|"sensitive data"| SEC
    DENV -->|"migrate"| CM
    style Docker fill:#f59e0b,color:#fff
    style K8s fill:#3b82f6,color:#fff
Docker Kubernetes Purpose
environment: ConfigMap General configuration values
.env file ConfigMap / Secret Settings and sensitive data
docker secret Secret Passwords, API keys, etc.

ConfigMap

A ConfigMap stores non-sensitive configuration data as key-value pairs.

Creating ConfigMaps

1. From YAML

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  APP_PORT: "3000"
  LOG_LEVEL: "info"
  DATABASE_HOST: "postgres-service"

2. From the command line

# From literal values
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=APP_PORT=3000

# From a file
kubectl create configmap nginx-config \
  --from-file=nginx.conf

# From a directory
kubectl create configmap configs \
  --from-file=./config-dir/

3. Storing entire config files

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    server {
        listen 80;
        server_name localhost;
        location / {
            root /usr/share/nginx/html;
            index index.html;
        }
    }

Using ConfigMaps

As environment variables

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: nginx:1.27
      envFrom:
        - configMapRef:
            name: app-config
      # Or individually
      env:
        - name: MY_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_ENV

As a volume mount

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  containers:
    - name: web
      image: nginx:1.27
      volumeMounts:
        - name: config-volume
          mountPath: /etc/nginx/conf.d
  volumes:
    - name: config-volume
      configMap:
        name: nginx-config

Volume-mounted ConfigMaps auto-update in the Pod (with a few minutes delay). Environment variables require a Pod restart.


Secret

A Secret stores sensitive data such as passwords, tokens, and keys.

ConfigMap vs. Secret

Aspect ConfigMap Secret
Purpose General configuration Sensitive data
Data format Plain text Base64 encoded
Size limit 1MiB 1MiB
Storage Disk tmpfs (memory)

Creating Secrets

1. From YAML

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: YWRtaW4=          # echo -n "admin" | base64
  password: c2VjcmV0MTIz      # echo -n "secret123" | base64

2. Using stringData (no Base64 needed)

apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
stringData:
  username: admin
  password: secret123

3. From the command line

kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=secret123

Using Secrets

As environment variables

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: my-app:1.0
      env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password

As a volume mount

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
    - name: app
      image: my-app:1.0
      volumeMounts:
        - name: secret-volume
          mountPath: /etc/secrets
          readOnly: true
  volumes:
    - name: secret-volume
      secret:
        secretName: db-secret

Secret Types

Type Purpose
Opaque Generic sensitive data (default)
kubernetes.io/dockerconfigjson Docker registry credentials
kubernetes.io/tls TLS certificate and private key
kubernetes.io/basic-auth Basic authentication credentials

Practical Example: Complete Configuration

Migrating a Docker Compose .env and environment configuration to Kubernetes.

Docker Compose (Source)

services:
  web:
    image: my-web-app:1.0
    environment:
      - APP_ENV=production
      - DB_HOST=db
      - DB_PORT=5432
      - DB_NAME=myapp
      - DB_USER=admin
      - DB_PASSWORD=secret123

Kubernetes (Target)

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  DB_HOST: "postgres-service"
  DB_PORT: "5432"
  DB_NAME: "myapp"
---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
stringData:
  DB_USER: admin
  DB_PASSWORD: secret123
---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: my-web-app:1.0
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: app-config
            - secretRef:
                name: app-secret
flowchart TB
    CM["ConfigMap\napp-config"] -->|"envFrom"| POD["Pod"]
    SEC["Secret\napp-secret"] -->|"envFrom"| POD
    POD -->|"DB_HOST=postgres-service"| DB["PostgreSQL\nService"]
    style CM fill:#3b82f6,color:#fff
    style SEC fill:#ef4444,color:#fff
    style POD fill:#22c55e,color:#fff

Managing ConfigMaps and Secrets

# ConfigMap operations
kubectl get configmaps
kubectl describe configmap app-config
kubectl get configmap app-config -o yaml

# Secret operations
kubectl get secrets
kubectl describe secret app-secret

# Decode a Secret value
kubectl get secret app-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 --decode

# Update
kubectl edit configmap app-config
kubectl edit secret app-secret

Secret Security Considerations

Important: Kubernetes Secrets are only Base64 encoded by default β€” they are NOT encrypted.

For production, consider these measures:

Measure Description
etcd encryption Encrypt Secret data stored in etcd
RBAC Control Secret access with role-based access control
External secret managers Integrate with AWS Secrets Manager, HashiCorp Vault, etc.
Keep Secrets out of Git Never commit Secret YAMLs to version control

Summary

Concept Description
ConfigMap Manages general config data as key-value pairs
Secret Manages sensitive data with Base64 encoding
envFrom Bulk inject ConfigMap/Secret as environment variables
Volume Mount Mount config files into Pods
stringData Write Secrets without Base64 encoding

Key Takeaways

  1. Separate configuration into ConfigMaps (general) and Secrets (sensitive)
  2. Use envFrom for bulk injection, valueFrom for individual keys
  3. Secrets are Base64 encoded, not encrypted β€” additional security measures are needed in production

Practice Exercises

Exercise 1: Basics

Split these settings into ConfigMap and Secret:

  • APP_NAME: "MyApp" (ConfigMap)
  • LOG_LEVEL: "debug" (ConfigMap)
  • API_KEY: "abc123xyz" (Secret)
  • JWT_SECRET: "my-jwt-secret" (Secret)

Exercise 2: File Mount

Store an Nginx configuration file (nginx.conf) in a ConfigMap and mount it into a Pod as a volume.

Challenge

Experiment with updating a ConfigMap and observe how changes propagate to existing Pods. Compare behavior between environment variable injection and volume mounts.


References


Next up: In Day 8, you'll learn about "Ingress and External Access" β€” more flexible HTTP routing than NodePort or LoadBalancer.