Skip to main content

Django Production Settings

Introduction

When deploying a Django application to production, you need to ensure that your settings are properly configured for security, performance, and reliability. The default settings provided by Django's startproject command are designed for development, not production use. This guide will walk you through the essential configuration changes needed to make your Django application production-ready.

Why Production Settings Matter

Development settings prioritize convenience and debugging capabilities, while production settings focus on:

  • Security: Protecting your application from attacks and data breaches
  • Performance: Optimizing resource usage and response times
  • Reliability: Ensuring stable operation under load
  • Scalability: Allowing your application to grow with increasing demand

Failing to configure production settings correctly can result in security vulnerabilities, slow performance, and potential data loss.

Setting Up a Production Configuration

Separating Development and Production Settings

A common practice is to have separate settings files for different environments. Let's explore how to structure this:

python
# Project structure
myproject/
├── myproject/
│ ├── __init__.py
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py

Base Settings (base.py)

python
# Common settings for all environments
import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Your apps here
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'myproject.urls'

TEMPLATES = [
# Template configuration
]

WSGI_APPLICATION = 'myproject.wsgi.application'

# Internationalization settings
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

# Static files
STATIC_URL = '/static/'

Development Settings (development.py)

python
from .base import *

DEBUG = True

SECRET_KEY = 'your-development-secret-key'

ALLOWED_HOSTS = ['localhost', '127.0.0.1']

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}

# Development-specific settings
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Production Settings (production.py)

python
from .base import *
import os

DEBUG = False

# Get secret key from environment variable
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

# Allow only your domain names
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

# Database configuration
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME'),
'USER': os.environ.get('DB_USER'),
'PASSWORD': os.environ.get('DB_PASSWORD'),
'HOST': os.environ.get('DB_HOST', 'localhost'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}

# SSL/HTTPS settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# Static files configuration
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

# Email configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.environ.get('EMAIL_HOST')
EMAIL_PORT = os.environ.get('EMAIL_PORT')
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = os.environ.get('DEFAULT_FROM_EMAIL')

# Logging configuration
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': os.path.join(BASE_DIR, 'django_error.log'),
'formatter': 'verbose',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'ERROR',
'propagate': True,
},
},
}

Using Environment Variables for Sensitive Data

To use the settings with environment variables, you'll need to set them in your production environment. For example, in a Linux/Unix system:

bash
export DJANGO_SECRET_KEY='your-very-secure-and-long-secret-key'
export DB_NAME='mydatabase'
export DB_USER='dbuser'
export DB_PASSWORD='dbpassword'

You can also use a .env file with a package like django-environ:

python
# Install django-environ first: pip install django-environ
import environ

env = environ.Env()
# Read .env file if it exists
environ.Env.read_env()

SECRET_KEY = env('DJANGO_SECRET_KEY')
DATABASES = {
'default': env.db(), # Expects DATABASE_URL environment variable
}

A sample .env file would look like:

DJANGO_SECRET_KEY=your-very-secure-and-long-secret-key
DATABASE_URL=postgres://dbuser:dbpassword@localhost:5432/mydatabase

Key Production Settings Explained

Security Settings

1. Debug Mode

python
DEBUG = False

Never leave debug mode on in production. It exposes sensitive information about your application.

2. Secret Key

python
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

Store your secret key in an environment variable, never hardcode it in your settings file.

3. Allowed Hosts

python
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

Specify the domains where your application will be served to prevent HTTP Host header attacks.

4. SSL/HTTPS Settings

python
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

These settings enforce HTTPS connections and implement security headers.

Performance Settings

1. Static Files

python
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

The ManifestStaticFilesStorage adds a hash to static filenames, which helps with cache busting.

2. Database Connection Pooling

For high-traffic sites, consider connection pooling:

python
DATABASES = {
'default': {
# ... other database settings ...
'CONN_MAX_AGE': 60, # Keep connections alive for 60 seconds
}
}

Caching

For production, you should implement a more robust caching system than the default:

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
}
}

Media Files Configuration

Handle user-uploaded files properly:

python
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Setting Up WSGI/ASGI for Production

Update your wsgi.py file to use the production settings:

python
import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production')

application = get_wsgi_application()

Similarly, update asgi.py if you're using ASGI:

python
import os
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production')

application = get_asgi_application()

Real-World Deployment Example

Let's walk through a complete example of deploying a Django application on a Linux server using Gunicorn and Nginx:

1. Set up your production settings file

Follow the structure outlined above to create your production.py settings.

2. Install production dependencies

bash
pip install gunicorn psycopg2-binary django-environ

3. Create a .env file with your production secrets

DJANGO_SECRET_KEY=your_secure_secret_key
DB_NAME=mydatabase
DB_USER=dbuser
DB_PASSWORD=dbpassword
DB_HOST=localhost
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
[email protected]
EMAIL_HOST_PASSWORD=emailpassword
[email protected]

4. Collect static files

bash
python manage.py collectstatic --settings=myproject.settings.production

5. Create a systemd service file for Gunicorn

Create a file at /etc/systemd/system/gunicorn-myproject.service:

ini
[Unit]
Description=gunicorn daemon for myproject
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/path/to/your/project
EnvironmentFile=/path/to/your/project/.env
ExecStart=/path/to/your/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/path/to/your/project/myproject.sock myproject.wsgi:application
Restart=on-failure

[Install]
WantedBy=multi-user.target

6. Configure Nginx as a reverse proxy

Create a file at /etc/nginx/sites-available/myproject:

nginx
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;

location = /favicon.ico { access_log off; log_not_found off; }

location /static/ {
root /path/to/your/project;
}

location /media/ {
root /path/to/your/project;
}

location / {
include proxy_params;
proxy_pass http://unix:/path/to/your/project/myproject.sock;
}
}

7. Enable the site and start the services

bash
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
sudo systemctl start gunicorn-myproject
sudo systemctl enable gunicorn-myproject
sudo systemctl restart nginx

8. Set up SSL with Certbot (Let's Encrypt)

bash
sudo apt-get install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Common Production Settings Issues and Troubleshooting

1. Static Files Not Loading

If your static files don't appear in production:

  • Check that you've run collectstatic
  • Verify your STATIC_ROOT and STATIC_URL settings
  • Ensure your web server is configured to serve files from the correct directory

2. Database Connection Issues

If you have trouble connecting to the database:

  • Confirm database credentials are correct
  • Check that the database server is accessible from your application server
  • Verify firewall settings allow connections on the database port

3. Email Not Sending

If emails aren't being sent:

  • Check your email server settings
  • Verify authentication credentials
  • Test with a simple mail command to isolate Django vs. server issues

Summary

Configuring Django for production is crucial for the security, performance, and reliability of your application. Key considerations include:

  • Separating development and production settings
  • Using environment variables for sensitive data
  • Configuring security settings to prevent common attacks
  • Setting up proper database connections
  • Configuring static and media files correctly
  • Implementing caching for better performance
  • Using a production-ready web server like Gunicorn behind a reverse proxy like Nginx

By following these best practices, you can ensure that your Django application is ready for production use.

Additional Resources

Practice Exercises

  1. Create a settings package with base, development, and production settings files for an existing Django project.
  2. Configure environment variables for database settings and test that your application connects correctly.
  3. Set up a simple Django application with Gunicorn and Nginx in a development environment to practice deployment.
  4. Implement a caching solution using Redis or Memcached in your Django application.
  5. Create a deployment checklist specific to your application, listing all the steps needed to deploy it to production.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)