Django cookbook · Beginner

Configure settings from environment variables

Keep secrets and per-environment values out of settings.py and fail fast when something required is missing.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Read configuration from environment variables in one place, with explicit types and defaults only for safe values. Missing secrets raise at startup instead of silently using a development value in production.

Solution

# settings.py
import os
from pathlib import Path

from django.core.exceptions import ImproperlyConfigured

BASE_DIR = Path(__file__).resolve().parent.parent


def env(name: str, default: str | None = None) -> str:
    value = os.environ.get(name, default)
    if value is None:
        raise ImproperlyConfigured(f"Set the {name} environment variable")
    return value


def env_bool(name: str, default: bool = False) -> bool:
    return env(name, str(default)).lower() in {"1", "true", "yes", "on"}


def env_list(name: str, default: str = "") -> list[str]:
    return [item.strip() for item in env(name, default).split(",") if item.strip()]


SECRET_KEY = env("DJANGO_SECRET_KEY")                  # required, no default
DEBUG = env_bool("DJANGO_DEBUG")
ALLOWED_HOSTS = env_list("DJANGO_ALLOWED_HOSTS", "localhost,127.0.0.1")
CSRF_TRUSTED_ORIGINS = env_list("DJANGO_CSRF_TRUSTED_ORIGINS")

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": env("POSTGRES_DB", "app"),
        "USER": env("POSTGRES_USER", "app"),
        "PASSWORD": env("POSTGRES_PASSWORD"),
        "HOST": env("POSTGRES_HOST", "localhost"),
        "PORT": env("POSTGRES_PORT", "5432"),
        "CONN_MAX_AGE": 60,
        "CONN_HEALTH_CHECKS": True,
    }
}

Pitfalls

  • Defaulting SECRET_KEY or DEBUG=True "for convenience"; a forgotten variable then ships insecure settings.
  • Parsing booleans with bool(os.environ["DEBUG"]), which is True for the string "False".
  • Committing .env files with real secrets.

Variations

  • Use django-environ or pydantic-settings for the same pattern with URL parsing (DATABASE_URL).
  • Run python manage.py check --deploy in CI with production-like variables.

Further reading: Django settings

Reference: https://docs.djangoproject.com/en/6.0/topics/settings/

More Django recipes

Was this page helpful?