Django
development settings
production settings
environment configuration
web development

Django How to manage development and production settings?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The safest way to manage Django settings is to keep shared defaults in one place and isolate environment-specific behavior behind separate settings modules. That gives you clear control over DEBUG, database credentials, allowed hosts, and service integrations without copying one huge settings file across development and production.

Split settings into base, development, and production

A common project layout looks like this:

python
1myproject/
2    settings/
3        __init__.py
4        base.py
5        development.py
6        production.py

base.py should hold the settings that are truly common across all environments, such as installed apps, middleware, templates, and the default time zone.

python
1from pathlib import Path
2import os
3
4BASE_DIR = Path(__file__).resolve().parent.parent.parent
5
6SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
7
8INSTALLED_APPS = [
9    "django.contrib.admin",
10    "django.contrib.auth",
11    "django.contrib.contenttypes",
12    "django.contrib.sessions",
13    "django.contrib.messages",
14    "django.contrib.staticfiles",
15]
16
17MIDDLEWARE = [
18    "django.middleware.security.SecurityMiddleware",
19    "django.contrib.sessions.middleware.SessionMiddleware",
20    "django.middleware.common.CommonMiddleware",
21    "django.middleware.csrf.CsrfViewMiddleware",
22    "django.contrib.auth.middleware.AuthenticationMiddleware",
23    "django.contrib.messages.middleware.MessageMiddleware",
24]

Then keep local-only behavior in development.py:

python
1from .base import *
2
3DEBUG = True
4ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
5
6DATABASES = {
7    "default": {
8        "ENGINE": "django.db.backends.sqlite3",
9        "NAME": BASE_DIR / "db.sqlite3",
10    }
11}

And production-only behavior in production.py:

python
1from .base import *
2import os
3
4DEBUG = False
5ALLOWED_HOSTS = os.environ["DJANGO_ALLOWED_HOSTS"].split(",")
6
7DATABASES = {
8    "default": {
9        "ENGINE": "django.db.backends.postgresql",
10        "NAME": os.environ["DB_NAME"],
11        "USER": os.environ["DB_USER"],
12        "PASSWORD": os.environ["DB_PASSWORD"],
13        "HOST": os.environ["DB_HOST"],
14        "PORT": os.environ.get("DB_PORT", "5432"),
15    }
16}
17
18SECURE_SSL_REDIRECT = True
19SESSION_COOKIE_SECURE = True
20CSRF_COOKIE_SECURE = True

This structure makes the differences obvious and prevents accidental production behavior from leaking into local development.

Select the active settings module explicitly

Django loads whichever module DJANGO_SETTINGS_MODULE points to. That means your environment selection should be explicit at process startup.

For local development:

bash
export DJANGO_SETTINGS_MODULE=myproject.settings.development
python manage.py runserver

For production:

bash
export DJANGO_SETTINGS_MODULE=myproject.settings.production
gunicorn myproject.wsgi:application

This is much cleaner than trying to detect the environment indirectly inside one giant settings file.

Put secrets in environment variables, not in Git

Do not hardcode production secrets into production.py. The settings module should define how to read secrets, not embed them directly. Environment variables are the simplest default and work well with Docker, Kubernetes, systemd, and most cloud platforms.

If you want cleaner parsing, packages such as django-environ can help, but the core rule remains the same: version-control the structure, not the secret values.

The same applies to service credentials for Redis, S3, email, and external APIs. Keep the interface in settings, but inject real values from the runtime environment.

Keep behavior differences deliberate

Not every setting belongs in every file. For example:

  • 'DEBUG should differ'
  • database settings often differ
  • cache backend may differ
  • logging usually differs
  • security flags should be strongest in production

What should stay shared is the application shape. Installed apps, middleware ordering, and template configuration usually belong in base.py unless there is a specific reason to diverge.

That balance keeps the code understandable. If too much logic is duplicated between development and production modules, the split stops helping.

Common Pitfalls

  • Keeping one giant settings file with if branches everywhere instead of using separate settings modules.
  • Committing production secrets directly into the repository.
  • Forgetting to set DJANGO_SETTINGS_MODULE explicitly and accidentally starting with the wrong environment.
  • Copying large blocks of settings between development and production until they drift out of sync.
  • Enabling DEBUG or weak security flags in production because local defaults leaked into deployment.

Summary

  • Use a shared base.py plus environment-specific modules such as development.py and production.py.
  • Select the active module with DJANGO_SETTINGS_MODULE.
  • Read secrets from environment variables rather than hardcoding them in settings files.
  • Keep common application structure in base.py and only isolate true environment differences.
  • The main goal is clarity: each environment should be explicit, reviewable, and hard to misuse.

Course illustration
Course illustration

All Rights Reserved.