Docs
/
Docker Kubernetes
Chapter 12

12 — Configuration & Secrets

ConfigMap

Store non-sensitive configuration data.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  DATABASE_HOST: "db-svc"
  config.json: |
    {
      "maxRetries": 3,
      "timeout": 5000
    }

Using ConfigMaps

# As environment variables
spec:
  containers:
    - name: api
      image: my-app:1.0.0
      envFrom:
        - configMapRef:
            name: app-config     # All keys as env vars

      # Or specific keys
      env:
        - name: LOG_LEVEL
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: LOG_LEVEL

      # As mounted file
      volumeMounts:
        - name: config
          mountPath: /app/config
  volumes:
    - name: config
      configMap:
        name: app-config
        items:
          - key: config.json
            path: config.json    # Mounted at /app/config/config.json

Secrets

Store sensitive data (base64-encoded, not encrypted by default).

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  DATABASE_PASSWORD: cGFzc3dvcmQxMjM=    # base64 of "password123"
  JWT_SECRET: bXktand0LXNlY3JldA==
# Create from command line
kubectl create secret generic app-secrets \
  --from-literal=DATABASE_PASSWORD=password123 \
  --from-literal=JWT_SECRET=my-jwt-secret

# Create from file
kubectl create secret generic tls-cert \
  --from-file=cert.pem --from-file=key.pem

Using Secrets

spec:
  containers:
    - name: api
      env:
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: DATABASE_PASSWORD

      # Or mount as files
      volumeMounts:
        - name: secrets
          mountPath: /run/secrets
          readOnly: true
  volumes:
    - name: secrets
      secret:
        secretName: app-secrets

Sealed Secrets (GitOps-Safe)

Regular Secrets can't be stored in Git (base64 ≠ encryption). SealedSecrets encrypt them.

# Install kubeseal CLI
# Encrypt secret (only cluster can decrypt)
kubeseal --format yaml < secret.yaml > sealed-secret.yaml

# sealed-secret.yaml is safe to commit to Git ✅
kubectl apply -f sealed-secret.yaml
# Controller decrypts → creates regular Secret in cluster

Full Example

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  NODE_ENV: production
  PORT: "3000"
  DB_HOST: postgres-svc
---
apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
type: Opaque
stringData:                     # stringData = plain text (auto-encoded)
  DB_PASSWORD: supersecret
  JWT_SECRET: my-jwt-key
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: my-app:1.0.0
          envFrom:
            - configMapRef:
                name: api-config
            - secretRef:
                name: api-secrets
          ports:
            - containerPort: 3000

Key Takeaways

  • ConfigMap for non-sensitive config; Secret for sensitive data
  • Use envFrom to inject all keys as env vars, or valueFrom for specific keys
  • Mount as files when config is a file format (JSON, YAML, .env)
  • Use stringData in Secrets for plain text (Kubernetes auto-encodes to base64)
  • K8s Secrets are base64, not encrypted — use SealedSecrets or external secret managers (Vault, AWS Secrets Manager) for production
  • Never commit plain Secrets to Git