Skip to main content
Kubernetes Essentials·Lesson 4 of 5

ConfigMaps & Secrets

Hardcoding configuration into container images makes them inflexible. ConfigMaps and Secrets let you decouple configuration from images, so the same image can run in development, staging, and production with different settings.

ConfigMaps

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

Create from the command line:

# From literal values
kubectl create configmap app-config \
  --from-literal=NODE_ENV=production \
  --from-literal=LOG_LEVEL=info \
  --from-literal=PORT=3000

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

# From an env file
kubectl create configmap app-config --from-env-file=.env.production

Create from YAML:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  PORT: "3000"
  DATABASE_HOST: "db-service"
  DATABASE_PORT: "5432"
  DATABASE_NAME: "myapp"
kubectl apply -f configmap.yaml

Using ConfigMaps in Pods

As environment variables:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web-app
          image: my-app:1.0
          # Load specific keys as environment variables
          env:
            - name: NODE_ENV
              valueFrom:
                configMapKeyRef:
                  name: app-config
                  key: NODE_ENV
            - name: LOG_LEVEL
              valueFrom:
                configMapKeyRef:
                  name: app-config
                  key: LOG_LEVEL

Load all keys as environment variables:

spec:
  containers:
    - name: web-app
      image: my-app:1.0
      envFrom:
        - configMapRef:
            name: app-config

As files mounted into the container:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  nginx.conf: |
    server {
        listen 80;
        server_name myapp.com;
        location / {
            proxy_pass http://api-service:3000;
        }
    }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          volumeMounts:
            - name: config
              mountPath: /etc/nginx/conf.d
      volumes:
        - name: config
          configMap:
            name: nginx-config

Secrets

Secrets store sensitive data like passwords, API keys, and TLS certificates. They are base64-encoded (not encrypted by default).

Create from the command line:

# From literal values
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=s3cur3p@ss

# From a file
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 myapp-tls \
  --cert=server.crt \
  --key=server.key

Create from YAML:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:
  username: admin
  password: s3cur3p@ss
  connection-string: "postgres://admin:s3cur3p@ss@db-service:5432/myapp"

Use stringData for plain text (Kubernetes encodes it automatically). Use data for pre-encoded base64:

echo -n "admin" | base64
# YWRtaW4=
echo -n "s3cur3p@ss" | base64
# czNjdXIzcEBzcw==
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=
  password: czNjdXIzcEBzcw==

Using Secrets in Pods

As environment variables:

spec:
  containers:
    - name: web-app
      image: my-app:1.0
      env:
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password

Load all keys from a secret:

spec:
  containers:
    - name: web-app
      image: my-app:1.0
      envFrom:
        - secretRef:
            name: db-credentials

As mounted files:

spec:
  containers:
    - name: web-app
      image: my-app:1.0
      volumeMounts:
        - name: credentials
          mountPath: /etc/secrets
          readOnly: true
  volumes:
    - name: credentials
      secret:
        secretName: db-credentials

The files /etc/secrets/username and /etc/secrets/password will contain the secret values.

Managing ConfigMaps and Secrets

# List ConfigMaps
kubectl get configmaps

# View ConfigMap contents
kubectl describe configmap app-config
kubectl get configmap app-config -o yaml

# List Secrets
kubectl get secrets

# View Secret contents (base64 encoded)
kubectl get secret db-credentials -o yaml

# Decode a secret value
kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 --decode

# Edit a ConfigMap
kubectl edit configmap app-config

# Delete a ConfigMap
kubectl delete configmap app-config

Updating Configuration

When you update a ConfigMap or Secret, pods using environment variables do not automatically pick up the changes — you need to restart them:

# Update the ConfigMap
kubectl apply -f configmap.yaml

# Restart the deployment to pick up changes
kubectl rollout restart deployment web-app

ConfigMaps mounted as volumes do update automatically (within 1-2 minutes), but your application needs to watch for file changes.

Immutable ConfigMaps and Secrets

For critical configuration that should never change accidentally:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-v2
immutable: true
data:
  NODE_ENV: "production"

Immutable ConfigMaps cannot be edited — you must create a new one and update your deployment to reference it.

Environment-Specific Configuration

Use Kustomize (built into kubectl) to manage different configurations per environment:

├── base/
   ├── deployment.yaml
   ├── service.yaml
   └── kustomization.yaml
├── overlays/
   ├── staging/
      ├── configmap.yaml
      └── kustomization.yaml
   └── production/
       ├── configmap.yaml
       └── kustomization.yaml

base/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml

overlays/production/kustomization.yaml:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - ../../base
patches:
  - configmap.yaml
# Apply staging configuration
kubectl apply -k overlays/staging/

# Apply production configuration
kubectl apply -k overlays/production/

Secret Management Best Practices

  1. Never commit secrets to Git. Use sealed-secrets or external secret managers.
  2. Use RBAC to restrict who can read secrets.
  3. Enable encryption at rest for etcd to protect stored secrets.
  4. Rotate secrets regularly and use immutable secrets for auditing.
  5. Use external secret managers in production (AWS Secrets Manager, HashiCorp Vault, Google Secret Manager).

External Secrets Operator example:

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: password
      remoteRef:
        key: production/database
        property: password

Summary

ConfigMaps store non-sensitive configuration. Secrets store sensitive data. Both can be injected as environment variables or mounted as files. You learned how to create, use, and update configuration, manage environment-specific settings with Kustomize, and follow security best practices. In the next lesson, you will learn about scaling and monitoring your applications.