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:
# 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
)
# 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
)
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
)
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:
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
:
# 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
DEBUG = False
Never leave debug mode on in production. It exposes sensitive information about your application.
2. Secret Key
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
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
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
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:
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:
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:
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:
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:
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
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
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
:
[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
:
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
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)
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
andSTATIC_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
- Django Deployment Checklist
- Django Security Documentation
- Django Performance and Optimization Guide
- Gunicorn Documentation
- Nginx Documentation
Practice Exercises
- Create a
settings
package with base, development, and production settings files for an existing Django project. - Configure environment variables for database settings and test that your application connects correctly.
- Set up a simple Django application with Gunicorn and Nginx in a development environment to practice deployment.
- Implement a caching solution using Redis or Memcached in your Django application.
- 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! :)