Manage Django Settings on Kubernetes With ConfigMaps and Secrets

Deliver Django configuration on Kubernetes: ConfigMaps for non-secret settings, Secrets for credentials, envFrom versus mounted files, rolling pods when values change, encryption at rest, and syncing from external secret managers.

Problem statement

A Django image should be the same in staging and production; what differs is configuration: ALLOWED_HOSTS, DATABASE_URL, SECRET_KEY, feature flags, email credentials. On Kubernetes, ConfigMaps hold non-sensitive settings and Secrets hold credentials, both injected as environment variables or files. Common problems: secrets committed to Git as base64 (which is not encryption), pods that keep old values after a change, and every pod getting every secret. A clear structure avoids them.

Config into Django podsConfigMaphosts, flags, levelsSecretkeys, passwordsPod specenvFrom or filesDjango settingsread envThe same image runs everywhere; ConfigMaps and Secrets supply what differs per environment.
  1. Put non-secret settings (DJANGO_SETTINGS_MODULE, ALLOWED_HOSTS, log level) in a ConfigMap.
  2. Put credentials (SECRET_KEY, DATABASE_URL, API keys) in a Secret, created from an external manager, SOPS, or Sealed Secrets, never committed in plain base64.
  3. Inject with envFrom (simple) or mount as files (supports updates without restarts if the app re-reads them).
  4. Trigger a rollout when config changes: hash the config into a pod annotation (Helm checksum/config) or use Reloader.
  5. Enable encryption at rest for Secrets in the cluster and restrict RBAC so only the app's service account reads them.
  6. Read settings in Django with typed helpers and fail fast on missing values.

Step-by-step solution

1. ConfigMap and Secret

apiVersion: v1
kind: ConfigMap
metadata: { name: django-config, namespace: app }
data:
  DJANGO_SETTINGS_MODULE: config.settings.production
  ALLOWED_HOSTS: example.com,www.example.com
  LOG_LEVEL: INFO
---
apiVersion: v1
kind: Secret
metadata: { name: django-secrets, namespace: app }
type: Opaque
stringData:
  SECRET_KEY: "replace-me"
  DATABASE_URL: "postgres://app:replace@db:5432/app?sslmode=require"

stringData accepts plain values and Kubernetes base64-encodes them; base64 is encoding, not encryption, so this manifest must not be committed with real values.

2. Inject into pods

containers:
  - name: web
    image: ghcr.io/you/myapp:SHA
    envFrom:
      - configMapRef: { name: django-config }
      - secretRef: { name: django-secrets }

Celery and migration Jobs use the same references so every process sees the same configuration. For values that may change without a restart (certificates, rotated tokens), mount Secrets as files instead; Kubernetes updates mounted files (not env vars) after a delay.

env vars vs files vs external syncUpdatesNotesenvFrom ConfigMap/Secretrestart needed✓simplest, most commonMounted Secret filesfiles refresh in placeapp must re-readExternal Secrets Operatorsyncs into Secrets✓source of truth outsideSecrets Store CSImounted at pod startno Secret object neededKeep the source of truth outside Git; let the cluster consume itdjango-deployment.com
Most Django apps read settings once at startup, so plan restarts on change.

3. Roll pods when config changes

Pods do not restart when a ConfigMap or Secret referenced by envFrom changes. Force a rollout:

# Helm deployment template
spec:
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}

or run kubectl rollout restart deploy/web after changes, or install a controller such as Stakater Reloader that restarts workloads when their referenced config changes.

4. Keep secrets out of Git

Options, from simplest to most integrated:

  • SOPS encrypted manifests decrypted in CI or by Flux/Argo CD plugins.
  • Sealed Secrets: encrypt with the cluster's public key; only the controller decrypts.
  • External Secrets Operator: sync from AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, or Vault.

5. Protect Secrets in the cluster

Enable encryption at rest for Secrets (managed clusters often do by default or with a KMS option), give the app's service account read access only to its own Secrets via RBAC, and avoid granting broad get secrets permissions to CI or humans.

config and secrets done rightConfigMap for settings, Secret for credentialsclear splitSame refs in web, workers, Jobsconsistent environmentRollout on config changechecksum annotation or ReloaderSecrets from SOPS, Sealed, or Externalnever plain base64 in GitEncryption at rest and tight RBACleast privilegeBase64 in a manifest is not a secret storedjango-deployment.com
Audit who can read Secrets in each namespace.

Verification check: kubectl exec deploy/web -- env | grep -c SECRET_KEY returns 1 without printing the value elsewhere, changing a ConfigMap value triggers new pods via your mechanism, Git history contains no plain secrets (gitleaks detect), and the app's service account cannot read other namespaces' Secrets.

Explanation

Kubernetes separates configuration from images through ConfigMaps and Secrets, which pods consume as environment variables or files. Secrets differ mainly in access control and handling (they can be encrypted at rest and are not shown in casual output), not in encoding. Environment variables are read when the process starts, so updates require new pods; files can update live. Sourcing secret values from an external manager or encrypted manifests keeps them out of Git while the cluster provides them to Django at runtime.

Edge cases / notes

  • Size limits. ConfigMaps and Secrets are limited to about 1 MiB each.
  • Immutable ConfigMaps (immutable: true) reduce API load and force deliberate rollouts with new names.
  • Environment variable leaks. Debug pages and crash reports can expose environment; keep DEBUG off and scrub reports.
  • Helm values files with secrets need the same protection as manifests.

Next steps

FAQ

Are Kubernetes Secrets encrypted?

Only if encryption at rest is enabled. The base64 in manifests is encoding, not encryption.

Do Django pods pick up changed Secrets automatically?

Not when injected as environment variables. Restart pods, use a checksum annotation, or a reloader controller.

Should Django settings come from ConfigMaps or Secrets?

Non-sensitive values from ConfigMaps; credentials from Secrets.

How do I keep Kubernetes secrets out of Git?

Use SOPS, Sealed Secrets, or the External Secrets Operator with a cloud secret manager.

Learning path · step 2 of 16

Django on Kubernetes

Keep going