Skip to main content
Kubernetes·8 min read

Kubernetes ConfigMaps & Secrets — Updating Config Without Restarting Every Pod

Learn to manage application configuration in Kubernetes with ConfigMaps and Secrets. Covers creation methods, volume mounts, environment variables, hot-reloading, and encryption at rest.

DT

DevOps Engineer & Technical Writer

The Problem

Your application needs database URLs, feature flags, API keys, and TLS certificates. Baking configuration into container images means rebuilding for every environment. Environment variables work but cannot be updated without restarting pods. You need a way to inject configuration that is environment-specific, updateable, and secure for sensitive values.

Kubernetes solves this with ConfigMaps (non-sensitive config) and Secrets (sensitive data).

ConfigMap / Secret Injection into Pods ConfigMap non-sensitive config Secret sensitive data Pod Container app process 1. Env Vars envFrom / valueFrom 2. Volume Mounts files at mountPath 3. CLI Args $(CONFIG_KEY) in args Volume mounts auto-update (~60s). Env vars require pod restart.

Creating ConfigMaps

From literal values

kubectl create configmap app-config \

--from-literal=LOG_LEVEL=info \

--from-literal=MAX_CONNECTIONS=100 \

--from-literal=FEATURE_NEW_UI=true

From files

kubectl create configmap app-config --from-file=config.properties

kubectl create configmap app-config --from-file=app.conf --from-file=logging.conf

kubectl create configmap nginx-config --from-file=nginx.conf=/path/to/custom-nginx.conf

From YAML manifest

apiVersion: v1

kind: ConfigMap

metadata:

name: app-config

namespace: production

data:

LOG_LEVEL: "info"

MAX_CONNECTIONS: "100"

FEATURE_NEW_UI: "true"

app.conf: |

server.port=8080

server.host=0.0.0.0

database.pool.size=20

cache.ttl=300

Creating Secrets

From literal values

kubectl create secret generic db-credentials \

--from-literal=username=appuser \

--from-literal=password='S3cur3P@ss!' \

--from-literal=host=db.internal.example.com

From files

kubectl create secret tls app-tls --cert=./tls.crt --key=./tls.key

kubectl create secret generic ssh-keys \

--from-file=id_rsa=./deploy_key \

--from-file=known_hosts=./known_hosts

From YAML with stringData

apiVersion: v1

kind: Secret

metadata:

name: db-credentials

namespace: production

type: Opaque

stringData:

username: appuser

password: "S3cur3P@ss!"

connection-string: "postgresql://appuser:S3cur3P@ss!@db.internal:5432/production"

Injecting as Environment Variables

All keys from a ConfigMap

apiVersion: apps/v1

kind: Deployment

metadata:

name: api-server

spec:

template:

spec:

containers:

- name: api

image: myapp:latest

envFrom:

- configMapRef:

name: app-config

- secretRef:

name: db-credentials

Specific keys

containers:

- name: api

image: myapp:latest

env:

- name: DATABASE_URL

valueFrom:

secretKeyRef:

name: db-credentials

key: connection-string

- name: LOG_LEVEL

valueFrom:

configMapKeyRef:

name: app-config

key: LOG_LEVEL

- name: CACHE_TTL

valueFrom:

configMapKeyRef:

name: app-config

key: CACHE_TTL

optional: true

Mounting as Volumes

Entire ConfigMap as directory

spec:

containers:

- name: api

image: myapp:latest

volumeMounts:

- name: config-volume

mountPath: /etc/app/config

readOnly: true

volumes:

- name: config-volume

configMap:

name: app-config

Specific keys as files

volumes:

- name: config-volume

configMap:

name: app-config

items:

- key: app.conf

path: application.conf

- key: logging.conf

path: log4j2.xml

Secrets with restricted permissions

volumes:

- name: tls-certs

secret:

secretName: app-tls

defaultMode: 0400

- name: db-creds

secret:

secretName: db-credentials

defaultMode: 0400

SubPath for single-file mounts

containers:

- name: nginx

volumeMounts:

- name: nginx-config

mountPath: /etc/nginx/nginx.conf

subPath: nginx.conf

volumes:

- name: nginx-config

configMap:

name: nginx-config

Note: subPath mounts do NOT receive automatic updates when the ConfigMap changes.

Updating Without Pod Restart

Volume-mounted ConfigMaps auto-update

When you update a ConfigMap, volume-mounted files update automatically within ~60 seconds:

kubectl edit configmap app-config

kubectl exec deploy/api-server -- cat /etc/app/config/LOG_LEVEL

Environment variables do NOT auto-update

Env vars are set at pod creation. You must restart:

kubectl rollout restart deployment/api-server

Hash annotation pattern for auto-rollout

spec:

template:

metadata:

annotations:

checksum/config: "{{ include (print $.Template.BasePath '/configmap.yaml') . | sha256sum }}"

When the ConfigMap changes, the hash annotation changes, triggering a new rollout.

Immutable ConfigMaps and Secrets

apiVersion: v1

kind: ConfigMap

metadata:

name: app-config-v2

immutable: true

data:

LOG_LEVEL: "info"

MAX_CONNECTIONS: "100"

To update, create a new ConfigMap with a different name and update your deployment reference.

Encryption at Rest

apiVersion: apiserver.config.k8s.io/v1

kind: EncryptionConfiguration

resources:

- resources:

- secrets

providers:

- aescbc:

keys:

- name: key1

secret: <base64-encoded-32-byte-key>

- identity: {}

For EKS, GKE, AKS — encryption at rest is handled by the cloud provider using KMS.

Viewing and Debugging

kubectl get configmap app-config -o yaml

kubectl describe configmap app-config

kubectl get secret db-credentials -o yaml

kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d

kubectl exec deploy/api-server -- env | sort

kubectl exec deploy/api-server -- ls -la /etc/app/config/

kubectl exec deploy/api-server -- cat /etc/app/config/app.conf

Common Mistakes

  • Using Secrets for non-sensitive data — Secrets add complexity. Use ConfigMaps for non-sensitive configuration.
  • Expecting env var updates without restart — Environment variables are set once at pod start. Only volume-mounted ConfigMaps auto-update.
  • Using subPath and expecting auto-updates — SubPath mounts are not updated when the source ConfigMap changes.
  • Committing Secret manifests to git — Even base64-encoded values are trivially decoded. Use sealed-secrets, SOPS, or external secret managers.
  • Not setting defaultMode on secret volumes — Default is 0644 (world-readable). Set to 0400 for sensitive files.
  • Exceeding the 1MB ConfigMap limit — ConfigMaps are limited to 1MiB. For larger data, use persistent volumes or init containers.
  • Quick Reference

    TaskCommand/Config
    Create from literal<code class="inline-code">kubectl create configmap name --from-literal=key=value</code>
    Create from file<code class="inline-code">kubectl create configmap name --from-file=file.conf</code>
    Create secret<code class="inline-code">kubectl create secret generic name --from-literal=key=value</code>
    View ConfigMap<code class="inline-code">kubectl get configmap name -o yaml</code>
    Decode secret<code class="inline-code">kubectl get secret name -o jsonpath=&#39;{.data.key}&#39; \base64 -d</code>
    Inject as env<code class="inline-code">envFrom: [configMapRef: {name: cm}]</code>
    Mount as volume<code class="inline-code">volumes: [{configMap: {name: cm}}]</code>
    Force restart<code class="inline-code">kubectl rollout restart deployment/name</code>
    Immutable config<code class="inline-code">immutable: true</code> in manifest

    Summary

    Use ConfigMaps for non-sensitive configuration and Secrets for credentials and keys. Mount as volumes when you need auto-updating configuration. Use environment variables for simple key-value injection that does not need runtime updates. Always encrypt secrets at rest and never commit Secret manifests to version control.

    ---

    Frequently Asked Questions

    What is the difference between ConfigMaps and Secrets in Kubernetes?

    ConfigMaps store non-sensitive configuration data as plain text, while Secrets store sensitive data base64-encoded and can be encrypted at rest. Both can be mounted as files or injected as environment variables. Use ConfigMaps for application config and Secrets for passwords, API keys, and certificates. Secrets have additional RBAC controls and can be managed by external secret operators.

    How do I update a ConfigMap without restarting pods?

    If the ConfigMap is mounted as a volume, Kubernetes automatically updates the mounted files within 1-2 minutes (the kubelet sync period). However, environment variable references are NOT updated without a pod restart. For immediate updates with volume mounts, use a sidecar that watches for changes or use stakater/Reloader to trigger rolling restarts automatically.

    How do I create a Secret from a file?

    Use kubectl create secret generic my-secret --from-file=./config.json to create a secret from a file, or --from-literal=password=mysecretvalue for individual values. For TLS certificates, use kubectl create secret tls my-tls --cert=cert.pem --key=key.pem. The file name becomes the key in the secret data map.

    Why is my Kubernetes Secret not actually secure?

    Kubernetes Secrets are only base64-encoded by default, which is not encryption. Anyone with read access to the namespace can decode them. To secure Secrets properly, enable encryption at rest in etcd, use RBAC to restrict access, and consider external secret managers like Vault or AWS Secrets Manager with the External Secrets Operator. Audit secret access through Kubernetes audit logs.

    ---