Preparing Django for Production

Introduction

Deploying a Django project into production is one of the most crucial steps in the web development lifecycle. While developing locally, you might be using Django’s built-in development server, SQLite database, and debug mode. However, once you move to production, the environment must be configured for security, scalability, and performance.

This detailed guide will walk you through all the essential steps to prepare your Django project for a real-world production deployment. You will learn how to set up your environment, secure sensitive information, configure databases, manage static files, enable caching, and apply best practices to make your Django application stable and secure in production.

Understanding Development vs. Production Environments

Before diving into configuration, it’s important to understand the difference between development and production environments.

  • Development Environment:
    Used for building and testing new features. Debugging and live error reporting are enabled to make it easier to identify problems.
  • Production Environment:
    The live environment where your users access your application. Security, performance, and stability are the priorities here.

Key differences:

FeatureDevelopmentProduction
Debug ModeEnabledDisabled
DatabaseUsually SQLitePostgreSQL or MySQL
Error ReportingDetailedHidden from users
SecurityRelaxedStrict
LoggingMinimalCentralized and structured
ServerrunserverGunicorn / uWSGI + Nginx

With this foundation, let’s begin preparing your Django project for production deployment.


Step 1: Setting Up a Virtual Environment

A virtual environment isolates your project’s dependencies, ensuring that package versions and libraries used in your project don’t conflict with others on the system.

Creating a Virtual Environment

To create a new virtual environment, navigate to your project directory and run:

python -m venv venv

This creates a folder named venv containing a clean Python environment.

Activating the Virtual Environment

To activate the environment, use the following command depending on your operating system:

For macOS/Linux:

source venv/bin/activate

For Windows:

venv\Scripts\activate

You should now see (venv) at the start of your terminal prompt, indicating that your virtual environment is active.


Step 2: Installing Project Dependencies

Once your virtual environment is active, install all required packages using the requirements.txt file.

pip install -r requirements.txt

This command ensures all dependencies used during development are installed in the virtual environment. If you don’t have a requirements.txt file yet, create one using:

pip freeze > requirements.txt

This file will list all packages and their versions, which is crucial for consistent deployment.


Step 3: Managing Environment Variables

Hardcoding sensitive information like secret keys, database passwords, and API credentials in settings.py is a major security risk. Instead, you should store them in environment variables.

Two popular Python packages for this are:

  • python-decouple
  • django-environ

Installing python-decouple

You can install it using pip:

pip install python-decouple

Creating a .env File

In your project root directory, create a file named .env and add your sensitive configuration values:

SECRET_KEY=your_production_secret_key_here
DEBUG=False
DATABASE_NAME=production_db
DATABASE_USER=db_user
DATABASE_PASSWORD=db_password
DATABASE_HOST=localhost
DATABASE_PORT=5432

Reading Environment Variables in Django

Update your settings.py to load variables from the .env file:

from decouple import config

SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)

This ensures that sensitive data is not exposed in your source code.


Step 4: Configuring the Database for Production

Django uses SQLite as the default database, which is fine for local development. However, for production environments, you need a more robust, scalable database like PostgreSQL or MySQL.

Using PostgreSQL

Install PostgreSQL and the required Python adapter:

pip install psycopg2-binary

Then, update your settings.py to use PostgreSQL:

DATABASES = {
'default': {
    'ENGINE': 'django.db.backends.postgresql',
    'NAME': config('DATABASE_NAME'),
    'USER': config('DATABASE_USER'),
    'PASSWORD': config('DATABASE_PASSWORD'),
    'HOST': config('DATABASE_HOST', default='localhost'),
    'PORT': config('DATABASE_PORT', default='5432'),
}
}

This connects your Django application to a PostgreSQL database configured via environment variables.


Step 5: Disabling Debug Mode

In development, you typically set DEBUG = True to view detailed error pages. However, this setting must never be enabled in production because it can expose sensitive system information to users.

In settings.py, use:

DEBUG = config('DEBUG', default=False, cast=bool)

Make sure your .env file includes:

DEBUG=False

When DEBUG is set to False, Django requires you to define the list of allowed hosts.


Step 6: Setting ALLOWED_HOSTS

The ALLOWED_HOSTS setting tells Django which domain names are valid for your site. This prevents HTTP Host header attacks.

Example:

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

For testing locally, you can use:

ALLOWED_HOSTS = ['*']

However, using '*' in production is not secure. Always specify exact hostnames for safety.


Step 7: Static and Media Files Configuration

Django does not serve static and media files automatically in production. You need to collect them and serve them using a web server like Nginx or AWS S3.

Configure Static Files in settings.py

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

Run the command below to collect all static files into STATIC_ROOT:

python manage.py collectstatic

This gathers all CSS, JS, and image files from each app into one central location for serving.


Step 8: Using Whitenoise to Serve Static Files (Optional)

If you want Django to serve static files without configuring Nginx initially, use Whitenoise.

Install it:

pip install whitenoise

Then modify your MIDDLEWARE in settings.py:

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
...
]

This setup allows Django to serve static files efficiently in a production environment without needing an external web server initially.


Step 9: Configuring Allowed Origins for CORS

If your Django backend serves a frontend (like React or Vue) hosted on a different domain, you must configure CORS (Cross-Origin Resource Sharing).

Install the CORS package:

pip install django-cors-headers

Add it to INSTALLED_APPS:

INSTALLED_APPS = [
...
'corsheaders',
]

Add the middleware:

MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]

Then configure allowed origins:

CORS_ALLOWED_ORIGINS = [
"https://yourfrontenddomain.com",
]

This ensures secure communication between your frontend and Django backend.


Step 10: Setting Up Logging

Logging helps you monitor errors and track what happens on your server. You should configure Django to log errors to files or external services like Sentry.

Example configuration in settings.py:

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
    'file': {
        'level': 'ERROR',
        'class': 'logging.FileHandler',
        'filename': BASE_DIR / 'logs/django_errors.log',
    },
},
'loggers': {
    'django': {
        'handlers': ['file'],
        'level': 'ERROR',
        'propagate': True,
    },
},
}

Now all errors will be logged to django_errors.log in the logs directory.


Step 11: Setting Up a Production Web Server (Gunicorn + Nginx)

The Django development server (runserver) is not suitable for production. Instead, you should use Gunicorn or uWSGI with Nginx.

Installing Gunicorn

pip install gunicorn

Run Gunicorn locally to test:

gunicorn myproject.wsgi:application

Configuring Nginx

Nginx acts as a reverse proxy that handles client requests and forwards them to Gunicorn. Example Nginx configuration:

server {
listen 80;
server_name yourdomain.com;
location /static/ {
    alias /path/to/your/project/staticfiles/;
}
location /media/ {
    alias /path/to/your/project/media/;
}
location / {
    proxy_pass http://127.0.0.1:8000;
    include proxy_params;
}
}

Restart Nginx after making changes:

sudo systemctl restart nginx

Step 12: Enabling HTTPS with SSL/TLS

Security is paramount. Always serve your site using HTTPS to protect user data. You can use Let’s Encrypt to get free SSL certificates.

Install Certbot:

sudo apt install certbot python3-certbot-nginx

Then obtain a certificate:

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot automatically configures HTTPS in your Nginx server block.


Step 13: Security Best Practices

Here are several essential security configurations you must enable in production.

1. Secure Cookies

SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

This ensures cookies are only sent over HTTPS.

2. Use X-Frame, X-Content, and X-XSS Headers

SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True

3. HTTP Strict Transport Security (HSTS)

SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

These settings force browsers to use HTTPS for your site.

4. Disable Clickjacking

X_FRAME_OPTIONS = 'DENY'

5. Hide Django Version Information

Remove the X-Powered-By header from your server to prevent disclosing framework details.


Step 14: Using Caching for Performance

Caching improves response times by storing copies of frequently accessed data.

Example: Using Memcached

Install Memcached and Django integration:

pip install python-memcached

Update your settings:

CACHES = {
'default': {
    'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
    'LOCATION': '127.0.0.1:11211',
}
}

You can now use caching in views or templates to reduce database load.


Step 15: Applying Database Migrations

Before running your production server, ensure your database schema is up to date:

python manage.py migrate

Also, collect static files and create a superuser:

python manage.py collectstatic
python manage.py createsuperuser

Step 16: Setting Up Cron Jobs or Celery for Background Tasks

If your application performs background tasks like sending emails or processing reports, use Celery with a broker like Redis.

Install Celery and Redis:

pip install celery redis

Configure Celery in your project’s __init__.py file, and set up Redis to handle background jobs efficiently.


Step 17: Monitoring and Logging Tools

To ensure smooth operation, use monitoring tools to track uptime and performance.

  • Sentry – Error tracking and reporting
  • Prometheus + Grafana – Metrics monitoring
  • ELK Stack (Elasticsearch, Logstash, Kibana) – Centralized logging

These tools help identify performance bottlenecks and production errors quickly.


Step 18: Running Django as a Systemd Service

You can configure Gunicorn to run as a service that restarts automatically on server reboot.

Create a file /etc/systemd/system/gunicorn.service:

[Unit]
Description=gunicorn daemon
After=network.target

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

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl enable gunicorn
sudo systemctl start gunicorn

Step 19: Testing Your Production Setup

Before going live, perform the following checks:

  1. Is DEBUG set to False?
  2. Are all environment variables loaded correctly?
  3. Are static files being served correctly?
  4. Does HTTPS work?
  5. Are logs being written?
  6. Is the database connection stable?

Use tools like:

  • curl -I yourdomain.com to test headers
  • ping and traceroute to test connectivity
  • Django’s check command:
python manage.py check --deploy

This built-in command identifies potential production misconfigurations.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *