Back to Kubernetes Course

Kubernetes Course

Why Externalize Configuration?

screenshot of the app

Container images should be environment-agnostic — the same image that runs in dev should run unchanged in production, with only the configuration differing. Baking environment-specific values (database URLs, feature flags, credentials) into the image means rebuilding it for every environment, which defeats the purpose of containerization. Kubernetes solves this with two objects: ConfigMaps for non-sensitive data, and Secrets for sensitive data.


ConfigMaps

A ConfigMap stores non-confidential key-value configuration data as a separate cluster object, decoupled from the Pod spec and the image.

apiVersion: v1 kind: ConfigMap metadata: name: app-config data: APP_ENV: "production" LOG_LEVEL: "info" application.properties: | server.port=8080 feature.new-checkout=true
kubectl create configmap app-config --from-literal=APP_ENV=production kubectl create configmap app-config --from-file=application.properties kubectl get configmap app-config -o yaml

Consuming a ConfigMap as environment variables:

spec: containers: - name: app image: myapp:1.0 envFrom: - configMapRef: name: app-config

Consuming a ConfigMap as a mounted file (useful for full config files, not just key-value pairs):

spec: containers: - name: app image: myapp:1.0 volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: app-config

With a mounted ConfigMap, each key becomes a file inside /etc/config, and the file's contents are the corresponding value — well suited for mounting an entire application.properties or nginx.conf file into a container unchanged.


Secrets

A Secret is structurally almost identical to a ConfigMap, but intended for sensitive values — passwords, tokens, TLS certificates, SSH keys.

apiVersion: v1 kind: Secret metadata: name: db-credentials type: Opaque data: username: YWRtaW4= # base64-encoded "admin" password: cGFzc3dvcmQxMjM= # base64-encoded "password123"
kubectl create secret generic db-credentials \ --from-literal=username=admin \ --from-literal=password=password123
spec: containers: - name: app image: myapp:1.0 env: - name: DB_USERNAME valueFrom: secretKeyRef: name: db-credentials key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: db-credentials key: password

Important — Secrets are base64-encoded, not encrypted, by default. Anyone with API access to read the Secret object (or etcd directly) can trivially decode it. Base64 is an encoding, not encryption — it exists so arbitrary binary data (like a certificate) can be safely represented as text in YAML.

  • To actually protect Secrets at rest, enable encryption at rest for etcd (an EncryptionConfiguration on the API server).
  • For production-grade secret management, most teams use external tools rather than raw Kubernetes Secrets: HashiCorp Vault, AWS Secrets Manager (via the External Secrets Operator), or cloud-native KMS integrations.
  • Restrict who can read Secret objects using RBAC (covered in a later lesson) — get/list on secrets should be tightly scoped.

Secret Types

| Type | Purpose | |---|---| | Opaque | Arbitrary user-defined key-value data (the default) | | kubernetes.io/tls | TLS certificate and private key, used by Ingress for HTTPS | | kubernetes.io/dockerconfigjson | Credentials for pulling images from a private container registry | | kubernetes.io/service-account-token | Automatically generated tokens for ServiceAccount authentication |

kubectl create secret tls tls-secret --cert=tls.crt --key=tls.key kubectl create secret docker-registry regcred \ --docker-server=myregistry.io \ --docker-username=myuser \ --docker-password=mypass

Live Updates and Reloading

When a ConfigMap or Secret mounted as a volume is updated, the file contents inside the Pod are updated automatically (usually within a minute), without restarting the Pod. However, most applications don't watch their config files for changes and won't pick up the new values until restarted — so many teams pair config updates with a rolling restart (kubectl rollout restart deployment/<name>) to guarantee the new configuration takes effect. Environment variables sourced from a ConfigMap/Secret, in contrast, are never updated live — changing them always requires a Pod restart.