Settings and environments

Django · Practical · 10 min

Misconfigured settings cause most deployment failures and security incidents in Django apps.

settings.py is a plain Python module selected by the DJANGO_SETTINGS_MODULE environment variable (manage.py, wsgi.py and asgi.py set a default). Key settings:

  • DEBUG: detailed error pages; must be False in production
  • SECRET_KEY: signs sessions, password reset tokens and more; keep it secret and stable
  • ALLOWED_HOSTS: host names the site may serve; required when DEBUG=False
  • DATABASES, CACHES, INSTALLED_APPS, MIDDLEWARE, TEMPLATES, STORAGES
  • TIME_ZONE and USE_TZ=True (the default): store aware datetimes in UTC

Keep secrets and per-environment values out of the repository. The simplest robust approach reads them from environment variables:

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]       # crash early if missing
DEBUG = os.environ.get("DJANGO_DEBUG", "") == "1"

Libraries like django-environ add helpers for parsing booleans, lists and database URLs. Some teams use split modules (settings/base.py, dev.py, prod.py) that import from a base; either way, production defaults should be safe.

In code, always read settings via from django.conf import settings, never by importing your settings module, so tests can override values with @override_settings and the right module is used.

python manage.py diffsettings shows what differs from Django's defaults, and python manage.py check --deploy lists insecure production settings.

Example

# mysite/settings.py (excerpt)
import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]                 # required
DEBUG = os.environ.get("DJANGO_DEBUG", "0") == "1"           # safe default: off
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost").split(",")

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ.get("POSTGRES_DB", "blog"),
        "USER": os.environ.get("POSTGRES_USER", "blog"),
        "PASSWORD": os.environ.get("POSTGRES_PASSWORD", ""),
        "HOST": os.environ.get("POSTGRES_HOST", "localhost"),
        "CONN_MAX_AGE": 60,                                  # reuse connections
    }
}

# anywhere else in the code:
from django.conf import settings
page_size = getattr(settings, "BLOG_PAGE_SIZE", 20)

Remember

  • DEBUG off, a secret SECRET_KEY and ALLOWED_HOSTS are mandatory in production
  • Read secrets and per-environment values from environment variables
  • Access settings via django.conf.settings
  • check --deploy audits production settings

Watch out for

  • Committing SECRET_KEY and DEBUG = True to the repository and deploying them.

Check yourself

  1. Why import settings with from django.conf import settings rather than import mysite.settings?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.djangoproject.com/en/6.0/topics/settings/