Deploying Django in Production.
Tags: django, nginx, webdev, python
Published: 2024-06-23 20:40:38


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r94ngxfek647u0zok8bn.png)

This 5-step tutorial will guide you through deploying a Django application using Gunicorn behind a reverse proxy (such as Nginx). 

Its important to note that this greatly depends on the needs of your project, but for most people, this is intended to be a minimal/initial setup. This configuration ensures that your application is served efficiently and securely, straight to production 🚀. 

## Prerequisites

Before starting, ensure you have the following:

- A domain name. (duh)
- A server with root access (e.g. an Ubuntu server).
- A Django application ready for deployment. (eg. being under **/opt/project_name**)
- A virtual environment. (eg. being under **/opt/project_name/venv**)
- Basic knowledge of Linux command-line operations.
- Nginx installed on your server.

## Step 0: Setting Up Your Domain and DNS

Before deploying your Django application, ensure your domain points to your server and DNS settings are correctly configured. Here’s a brief guide on how to do this:

1. **Domain Registration:**
   - Register your domain with a domain registrar.

2. **Obtain Server IP Address:**
   - Get the public IP address of your server. This is the address that clients will use to access your application.

3. **DNS Configuration:**
   - Log in to your domain registrar’s control panel and find the DNS settings for your domain.
   - Add an A record to point your domain to your server's IP address:
     - **Type:** A
     - **Name:** @ (this represents your domain, e.g., `your_domain.com`)
     - **Value:** [Your Server's IP Address]
     - **TTL:** Default or 3600 seconds (1 hour)
   - If you want to use `www.your_domain.com`, add a CNAME record:
     - **Type:** CNAME
     - **Name:** www
     - **Value:** your_domain.com
     - **TTL:** Default or 3600 seconds (1 hour)

4. **Propagation Time:**
   - DNS changes may take some time to propagate (up to 48 hours, but usually within a few hours). You can use tools like `whatsmydns.net` to check the propagation status.

5. **Verify DNS Settings:**
   - After DNS propagation, you should be able to verify your domain points to your server by using tools like `ping` or `dig`:
     ```bash
     ping your_domain.com
     ```
   - You should see responses from your server's IP address.

6. **Firewall and Security Group Configuration:**
   - Ensure your server's firewall and any cloud provider security groups allow traffic on ports 80 (HTTP) and 443 (HTTPS).

Once your DNS is set up and pointing to your server, you can proceed with the deployment steps outlined in the tutorial. This setup ensures that visitors accessing your domain will be directed to your server, where Nginx and Gunicorn will handle the requests.

## Step 1: Install Gunicorn

First, activate your virtual environment and install Gunicorn:

```bash
source /opt/project_name/venv/bin/activate
python3 -m pip install gunicorn
```

## Step 2: Configure Gunicorn

Create a Gunicorn configuration file (optional but recommended) to manage settings like worker processes. Create a file named `gunicorn_config.py` in your project directory:

```python
# gunicorn_config.py

bind = "127.0.0.1:8000"
workers = 3
```

## Step 3: Adjust Django Settings

Update your Django settings to handle the proxy setup correctly. Edit your `settings.py` file to include the following:

```python
# settings.py

# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

# Media files (Uploaded by users)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# Specifies a list of valid host/domain names for the Django site, providing protection against HTTP Host header attacks.
ALLOWED_HOSTS = ['your_domain.com', 'www.your_domain.com', 'another_domain.com']

# Tells Django to use the X-Forwarded-Host header from the proxy, allowing it to know the original host requested by the client.
USE_X_FORWARDED_HOST = True
# Tells Django to use the X-Forwarded-Port header from the proxy, indicating the port number used by the client.
USE_X_FORWARDED_PORT = True
# Instructs Django to trust the X-Forwarded-Proto header, which is set by the proxy server, to determine whether the request is secure (HTTPS).
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

# Forces all HTTP requests to be redirected to HTTPS.
SECURE_SSL_REDIRECT = True

# Ensures that the CSRF cookie is only sent over HTTPS connections.
CSRF_COOKIE_SECURE = True
# Ensures that the session cookie is only sent over HTTPS connections.
SESSION_COOKIE_SECURE = True

# Enables HTTP Strict Transport Security (HSTS) for the specified duration (in seconds), forcing browsers to only connect via HTTPS.
SECURE_HSTS_SECONDS = 31536000
# Applies HSTS policy to all subdomains.
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
# Allows the domain to be included in browsers' HSTS preload list, ensuring maximum protection.
SECURE_HSTS_PRELOAD = True

# Enables the X-Content-Type-Options header, preventing browsers from MIME-sniffing a response away from the declared content-type.
SECURE_CONTENT_TYPE_NOSNIFF = True
# Controls the information sent in the Referer header, improving privacy and security by not sending the referrer from HTTPS to HTTP.
SECURE_REFERRER_POLICY = 'no-referrer-when-downgrade'

# SECURE_BROWSER_XSS_FILTER = True
# [Deprecated on 4.0]
# Enables the X-XSS-Protection header, which tells browsers to block detected cross-site scripting (XSS) attacks.

```

##### Collect Static Files

Django comes with a handy command to collect all static files into the directory specified in STATIC_ROOT.

```bash
python3 -m /opt/project_name/manage.py collectstatic
```

## Step 4: Create a Systemd Service for Gunicorn

Create a systemd service file to manage the Gunicorn process. Create a file named `gunicorn.service` in `/etc/systemd/system/`:

```ini
# /etc/systemd/system/gunicorn.service

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

[Service]
User=yourusername
Group=www-data
WorkingDirectory=/opt/project_name
ExecStart=/opt/project_name/venv/bin/gunicorn --config /opt/gunicorn_config.py project_name.wsgi:application

[Install]
WantedBy=multi-user.target
```

Replace `/opt/project_name`, `/opt/project_name/venv`, and `project_name` with your actual project paths and names.

Reload the systemd daemon and start the Gunicorn service:

```bash
sudo systemctl daemon-reload
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
```

## Step 5: Configure Nginx

Create an Nginx configuration file to proxy requests to Gunicorn. Create a file named `project_name` in `/etc/nginx/sites-available/`:

```nginx
# /etc/nginx/sites-available/project_name

server {
    listen 80;
    server_name your_domain.com www.your_domain.com another_domain.com;

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

    location /static/ {
        alias /opt/project_name/static/;  # Must be the same as STATIC_ROOT
    }

    location /media/ {
        alias /opt/project_name/media/;  # Must be the same as MEDIA_ROOT
        access_log /var/log/nginx/media_access.log;
        error_log /var/log/nginx/media_error.log;
    }

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8000;

        access_log /var/log/nginx/django_access.log;
        error_log /var/log/nginx/django_error.log;
    }

    listen [::]:80;
}
```

Create a symbolic link to enable the site, and ensure the static and media directories, as well as the log directories, have the correct permissions. 

You need to make sure that the user running Nginx has the necessary read/write permissions.

```bash
# Create directories if they don't exist
sudo mkdir -p /opt/project_name/static /opt/project_name/media /var/log/nginx

# Set ownership (assuming nginx user and group) and permissions
sudo chown -R nginx:nginx /opt/project_name/static /opt/project_name/media /var/log/nginx
sudo chmod -R 755 /opt/project_name/static /opt/project_name/media /var/log/nginx

sudo ln -s /etc/nginx/sites-available/project_name /etc/nginx/sites-enabled
```

Test the Nginx configuration and restart the service:

```bash
sudo nginx -t
sudo systemctl restart nginx
```

## Step 6: Configure HTTPS

It's highly recommended to secure your application with HTTPS. Use Certbot to obtain a free SSL certificate:

```bash
sudo apt-get install certbot python3-certbot-nginx
sudo certbot --nginx -d your_domain.com -d www.your_domain.com -d another_domain.com
```

Follow the prompts to configure SSL.

## Conclusion

Your Django application is now deployed using Gunicorn behind an Nginx reverse proxy.

However, there are several ways you could customize and enhance this deployment:
- **Load Balancing**: If you anticipate high traffic, consider setting up multiple Gunicorn instances behind a load balancer to distribute the load evenly.
- **Containerization**: Using Docker to containerize your application can simplify deployment and provide greater consistency across different environments.
- **Advanced Security Configurations**: Additional security measures such as setting up a Web Application Firewall (WAF), regular security audits, and implementing stricter Content Security Policies (CSP) can further protect your application.

Be sure to explore these options and refer to the official documentation for Gunicorn, Nginx, and Django for further customization and advanced configurations. By continuously refining your deployment setup, you can ensure your Django application remains secure, efficient, and scalable.