Hardcoding configuration in containers is bad. Environment variables are better but still limited. ConfigMaps and Secrets are Kubernetes' native solution for configuration management. ConfigMaps store non-sensitive data. Secrets store sensitive data like passwords and API keys. Both can be mounted as files or injected as environment variables.

1. Learning Objectives

By the end of this lesson, you will be able to:

  • Create ConfigMaps from literal values, files, and directories
  • Inject ConfigMap data as environment variables
  • Mount ConfigMaps as volumes
  • Create and manage Secrets for sensitive data
  • Use Secrets as environment variables or mounted files
  • Update configurations without rebuilding images

2. Why This Matters

Real-world scenario: Your application needs different database hosts, API endpoints, and feature flags for dev, staging, and production. Instead of rebuilding images for each environment, ConfigMaps let you inject configuration at runtime.

3. Core Concepts

Creating ConfigMaps

# Create from literal values
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=LOG_LEVEL=info \
  --from-literal=MAX_CONNECTIONS=100

# Create from file
kubectl create configmap app-config --from-file=./app.conf
kubectl create configmap app-config --from-file=config=./app.conf

# Create from directory (all files in directory)
kubectl create configmap app-config --from-file=./configs/

# Create from env file
kubectl create configmap app-config --from-env-file=.env

# Create from YAML definition
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: production
  LOG_LEVEL: info
  MAX_CONNECTIONS: "100"
  database.conf: |
    host: postgres-service
    port: 5432
    name: mydb
  nginx.conf: |
    server {
      listen 80;
      location / {
        proxy_pass http://app:3000;
      }
    }
EOF

# View ConfigMaps
kubectl get configmaps
kubectl describe configmap app-config
kubectl get configmap app-config -o yaml
Creating ConfigMaps

Using ConfigMaps as Environment Variables

# deployment-with-configmap-env.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: myapp:latest
        env:
        # Single value from ConfigMap
        - name: APP_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_ENV
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: LOG_LEVEL
        # All values from ConfigMap
        envFrom:
        - configMapRef:
            name: app-config
        - configMapRef:
            name: database-config
        ports:
        - containerPort: 8080
Injecting ConfigMaps as environment variables

Using ConfigMaps as Volumes

# deployment-with-configmap-volume.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        volumeMounts:
        - name: nginx-config
          mountPath: /etc/nginx/conf.d
          readOnly: true
        - name: app-config
          mountPath: /app/config
          readOnly: true
      volumes:
      - name: nginx-config
        configMap:
          name: nginx-config
      - name: app-config
        configMap:
          name: app-config
          # Mount specific items
          items:
          - key: database.conf
            path: database.yaml
          - key: app.properties
            path: application.properties
Mounting ConfigMaps as volumes

Secrets (Sensitive Data)

# Create secrets
# From literal
kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=Sup3rSecret!

# From file
kubectl create secret generic tls-secret \
  --from-file=tls.crt=./server.crt \
  --from-file=tls.key=./server.key

# From env file
kubectl create secret generic app-secrets --from-env-file=.secrets

# From YAML (base64 encoded)
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  username: YWRtaW4=      # echo -n 'admin' | base64
  password: U3VwM3JTZWNyZXQh  # echo -n 'Sup3rSecret!' | base64
EOF

# Encode/decode base64
echo -n 'myvalue' | base64
echo 'bXl2YWx1ZQ==' | base64 -d

# View secrets
kubectl get secrets
kubectl describe secret db-secret
kubectl get secret db-secret -o yaml
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d
Creating and managing Secrets
# deployment-with-secrets.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-with-secrets
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: myapp:latest
        env:
        # Secret as environment variable
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: password
        envFrom:
        - secretRef:
            name: app-secrets
        # Secret mounted as volume
        volumeMounts:
        - name: tls-certs
          mountPath: /etc/ssl/certs
          readOnly: true
      volumes:
      - name: tls-certs
        secret:
          secretName: tls-secret
          defaultMode: 0400  # Read-only for security
Using Secrets in deployments

Secret Types

# Opaque - Generic secret (default)
apiVersion: v1
kind: Secret
metadata:
  name: generic-secret
type: Opaque
data:
  key: dmFsdWU=

# kubernetes.io/service-account-token
# Automatically created for service accounts

# kubernetes.io/dockercfg / kubernetes.io/dockerconfigjson
# For private container registry authentication
apiVersion: v1
kind: Secret
metadata:
  name: regcred
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsiYXV0aCI6Ik1HVXpaak5rTkdZdFkyUTJZeTAwTTJKa0xXRmtOV1l0WkRjeU1tWmlNV1ZqT1RjPSJ9fX0=

# kubernetes.io/tls - TLS certificates
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTiBDRV...
  tls.key: LS0tLS1CRUdJTiBSU0Eg...

# Use in Ingress
# spec:
#   tls:
#   - hosts:
#     - example.com
#     secretName: tls-secret

# bootstrap.kubernetes.io/token - Bootstrap tokens
# Used for node bootstrapping
Secret types

4. Complete Project: Configuration Management

# complete-config.yaml
---
# Database ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: database-config
data:
  DB_HOST: postgres-service
  DB_PORT: "5432"
  DB_NAME: myapp
  DB_POOL_SIZE: "10"
  postgresql.conf: |
    max_connections = 100
    shared_buffers = 128MB
    effective_cache_size = 384MB

---
# Application ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: production
  LOG_LEVEL: INFO
  REDIS_HOST: redis-service
  REDIS_PORT: "6379"
  CACHE_TTL: "300"
  FEATURE_FLAGS: |
    new_dashboard: true
    beta_features: false
    analytics: true
  application.yml: |
    server:
      port: 8080
      compression:
        enabled: true
    spring:
      datasource:
        url: jdbc:postgresql://postgres-service:5432/myapp
        hikari:
          maximum-pool-size: 10

---
# Database Secret
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  DB_USER: cG9zdGdyZXM=  # postgres
  DB_PASSWORD: c3VwZXJzZWNyZXQ=  # supersecret

---
# API Secret
apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
type: Opaque
data:
  API_KEY: YWJjMTIzNDU2Nzg5MGFiY2RlZg==
  JWT_SECRET: eHl6Nzg5MGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6

---
# TLS Secret for Ingress
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTiBDRV...
  tls.key: LS0tLS1CRUdJTiBSU0Eg...

---
# Application Deployment using all config
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: app
        image: myapp:latest
        ports:
        - containerPort: 8080
        # Environment variables from ConfigMaps
        envFrom:
        - configMapRef:
            name: database-config
        - configMapRef:
            name: app-config
        # Environment variables from Secrets
        env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: DB_USER
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: DB_PASSWORD
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: api-secrets
              key: API_KEY
        # Config files mounted as volumes
        volumeMounts:
        - name: app-config-volume
          mountPath: /app/config
          readOnly: true
        - name: postgres-config
          mountPath: /etc/postgresql
          readOnly: true
      volumes:
      - name: app-config-volume
        configMap:
          name: app-config
      - name: postgres-config
        configMap:
          name: database-config

---
# Ingress with TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: main-ingress
spec:
  tls:
  - hosts:
    - app.example.com
    secretName: tls-secret
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 80

# Apply all
kubectl apply -f complete-config.yaml

# Verify configuration
kubectl get configmaps
kubectl get secrets
kubectl describe pod -l app=myapp
kubectl exec deployment/app-deployment -- env | grep -E "DB_|APP_|REDIS"
kubectl exec deployment/app-deployment -- cat /app/config/application.yml
Complete configuration management with ConfigMaps and Secrets

5. Common Errors & Solutions

6. Summary Checklist

7. Next Steps

Next lesson: Kubernetes Lesson 6.5: Persistent Volumes (PVCs)

Gataya Med

DevOps Engineer & Backend Developer. Sharing insights on cloud, automation, and scalable systems.

Comments (0)

Sarah Chen February 10, 2025

This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!

Reply

Leave a Comment