Skip to content

Chapter 17: Deployment

17.2 Deployment Solutions

In the previous section, we learned how to configure the Django production environment. This section will introduce several common Django deployment solutions, including the traditional deployment using Nginx + Gunicorn, cloud platform deployment, and static file CDN acceleration.

Nginx + Gunicorn

Nginx + Gunicorn is the most common deployment combination for Django applications. Nginx acts as a reverse proxy server to handle static files and load balancing, while Gunicorn serves as the WSGI server to run the Django application.

Gunicorn Configuration

First, install Gunicorn:

bash
pip install gunicorn

Create a Gunicorn configuration file:

python
# gunicorn.conf.py
import multiprocessing

# Server socket
bind = "127.0.0.1:8000"
backlog = 2048

# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2

# Restart strategy
max_requests = 1000
max_requests_jitter = 100

# Logging
loglevel = "info"
accesslog = "/var/log/gunicorn/access.log"
errorlog = "/var/log/gunicorn/error.log"
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'

# Process naming
proc_name = "myproject"

# Server mechanics
preload_app = True
daemon = False
pidfile = "/var/run/gunicorn.pid"
user = "www-data"
group = "www-data"

# SSL (if using Gunicorn to handle HTTPS directly)
# keyfile = "/path/to/keyfile"
# certfile = "/path/to/certfile"

# Performance tuning
worker_tmp_dir = "/dev/shm"

Start Gunicorn:

bash
# Start with configuration file
gunicorn --config gunicorn.conf.py myproject.wsgi:application

# Or start directly
gunicorn myproject.wsgi:application --bind 127.0.0.1:8000 --workers 3

Nginx Configuration

Install Nginx:

bash
# Ubuntu/Debian
sudo apt update
sudo apt install nginx

# CentOS/RHEL
sudo yum install nginx

Create Nginx site configuration:

nginx
# /etc/nginx/sites-available/myproject
upstream app_server {
    server 127.0.0.1:8000 fail_timeout=0;
}

server {
    listen 80;
    server_name yourdomain.com;
    
    # Redirect to HTTPS (if SSL is configured)
    # return 301 https://$server_name$request_uri;
    
    client_max_body_size 10M;
    
    # Static file handling
    location /static/ {
        alias /var/www/myproject/staticfiles/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    location /media/ {
        alias /var/www/myproject/media/;
        expires 30d;
        add_header Cache-Control "public";
    }
    
    # Forward dynamic requests to Gunicorn
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app_server;
    }
    
    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
}

# HTTPS configuration (if needed)
server {
    listen 443 ssl http2;
    server_name yourdomain.com;
    
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;
    
    # SSL security configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    
    # Enable HSTS
    add_header Strict-Transport-Security "max-age=63072000" always;
    
    # Other configuration same as HTTP server
    client_max_body_size 10M;
    
    location /static/ {
        alias /var/www/myproject/staticfiles/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    location /media/ {
        alias /var/www/myproject/media/;
        expires 30d;
        add_header Cache-Control "public";
    }
    
    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app_server;
    }
}

Enable the site and restart Nginx:

bash
# Enable site
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Restart Nginx
sudo systemctl restart nginx

System Service Configuration

To ensure Gunicorn automatically runs at system startup, we can create a systemd service:

ini
# /etc/systemd/system/myproject.service
[Unit]
Description=MyProject Django Application
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
ExecStart=/var/www/myproject/venv/bin/gunicorn --config gunicorn.conf.py myproject.wsgi:application
ExecReload=/bin/kill -s HUP $MAINPID
Restart=always

# Environment variables
Environment=DJANGO_ENV=production
Environment=DJANGO_SECRET_KEY=your-secret-key
Environment=DB_PASSWORD=your-db-password

[Install]
WantedBy=multi-user.target

Start and enable the service:

bash
# Reload systemd configuration
sudo systemctl daemon-reload

# Start service
sudo systemctl start myproject

# Set to start on boot
sudo systemctl enable myproject

# Check service status
sudo systemctl status myproject

Cloud Platform Deployment

Modern Django applications can also be deployed to various cloud platforms, including Heroku, AWS, Google Cloud, Azure, etc.

Heroku Deployment

  1. Create a Procfile:
web: gunicorn myproject.wsgi:application --log-file -
  1. Create runtime.txt to specify Python version:
python-3.11.5
  1. Install and configure django-heroku:
bash
pip install django-heroku
python
# settings/production.py
import django_heroku

# Activate Heroku configuration at the end of the file
django_heroku.settings(locals())
  1. Deploy to Heroku:
bash
# Install Heroku CLI and login
heroku login

# Create application
heroku create myproject

# Set environment variables
heroku config:set DJANGO_ENV=production
heroku config:set DJANGO_SECRET_KEY=your-secret-key

# Deploy code
git push heroku main

Docker Deployment

Create a Dockerfile:

dockerfile
# Dockerfile
FROM python:3.11-slim

# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        postgresql-client \
    && rm -rf /var/lib/apt/lists/*

# Copy dependency file
COPY requirements.txt /app/

# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy project files
COPY . /app/

# Create non-root user
RUN adduser --disabled-password --gecos '' appuser && \
    chown -R appuser:appuser /app
USER appuser

# Expose port
EXPOSE 8000

# Run application
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

Create docker-compose.yml:

yaml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DJANGO_ENV=production
      - DJANGO_SECRET_KEY=your-secret-key
    volumes:
      - static_volume:/var/www/myproject/staticfiles
      - media_volume:/var/www/myproject/media
    depends_on:
      - db
      - redis

  db:
    image: postgres:15
    environment:
      - POSTGRES_DB=myproject
      - POSTGRES_USER=myproject
      - POSTGRES_PASSWORD=your-db-password
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - static_volume:/var/www/myproject/staticfiles
      - media_volume:/var/www/myproject/media
    depends_on:
      - web

volumes:
  postgres_data:
  static_volume:
  media_volume:

Static File CDN

To improve the loading speed of static files, static files can be deployed to a CDN.

Using AWS S3 and CloudFront

  1. Install dependencies:
bash
pip install django-storages boto3
  1. Configure settings:
python
# settings/production.py
import os

# AWS configuration
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')
AWS_S3_REGION_NAME = os.environ.get('AWS_S3_REGION_NAME', 'us-east-1')
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'

# Static file storage
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/static/'

# Media file storage
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/media/'

# S3 settings
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
AWS_S3_VERIFY = True
  1. Collect static files:
bash
python manage.py collectstatic --noinput

Using Alibaba Cloud OSS

  1. Install dependencies:
bash
pip install django-oss-storage
  1. Configure settings:
python
# settings/production.py
import os

# Alibaba Cloud OSS configuration
OSS_ACCESS_KEY_ID = os.environ.get('OSS_ACCESS_KEY_ID')
OSS_ACCESS_KEY_SECRET = os.environ.get('OSS_ACCESS_KEY_SECRET')
OSS_BUCKET_NAME = os.environ.get('OSS_BUCKET_NAME')
OSS_ENDPOINT = os.environ.get('OSS_ENDPOINT')

# Static file storage
STATICFILES_STORAGE = 'django_oss_storage.backends.OssStaticStorage'
STATIC_URL = f'https://{OSS_BUCKET_NAME}.{OSS_ENDPOINT}/static/'

# Media file storage
DEFAULT_FILE_STORAGE = 'django_oss_storage.backends.OssMediaStorage'
MEDIA_URL = f'https://{OSS_BUCKET_NAME}.{OSS_ENDPOINT}/media/'

Using Tencent Cloud COS

  1. Install dependencies:
bash
pip install qcloud_cos django-cos-storage
  1. Configure settings:
python
# settings/production.py
import os

# Tencent Cloud COS configuration
COS_SECRET_ID = os.environ.get('COS_SECRET_ID')
COS_SECRET_KEY = os.environ.get('COS_SECRET_KEY')
COS_REGION = os.environ.get('COS_REGION')
COS_BUCKET = os.environ.get('COS_BUCKET')

# Static file storage
STATICFILES_STORAGE = 'django_cos_storage.backends.CosStaticStorage'
STATIC_URL = f'https://{COS_BUCKET}.cos.{COS_REGION}.myqcloud.com/static/'

# Media file storage
DEFAULT_FILE_STORAGE = 'django_cos_storage.backends.CosMediaStorage'
MEDIA_URL = f'https://{COS_BUCKET}.cos.{COS_REGION}.myqcloud.com/media/'

Monitoring and Maintenance

After deployment, application monitoring and maintenance also need to be considered.

Health Check Endpoint

Create a health check view:

python
# views.py
from django.http import JsonResponse
from django.db import connections
from django.db.utils import OperationalError
import redis
import os

def health_check(request):
    """Health check endpoint"""
    health_status = {
        'status': 'healthy',
        'database': check_database(),
        'redis': check_redis(),
        'disk_space': check_disk_space(),
    }
    
    # Return error status if any component is unhealthy
    if not all(health_status.values()):
        health_status['status'] = 'unhealthy'
        return JsonResponse(health_status, status=503)
    
    return JsonResponse(health_status)

def check_database():
    """Check database connection"""
    db_conn = connections['default']
    try:
        c = db_conn.cursor()
        return True
    except OperationalError:
        return False

def check_redis():
    """Check Redis connection"""
    try:
        redis_client = redis.Redis(
            host=os.environ.get('REDIS_HOST', 'localhost'),
            port=os.environ.get('REDIS_PORT', 6379),
            db=0
        )
        redis_client.ping()
        return True
    except:
        return False

def check_disk_space():
    """Check disk space"""
    try:
        stat = os.statvfs('/')
        free_bytes = stat.f_bavail * stat.f_frsize
        # Consider unhealthy if free space is less than 1GB
        return free_bytes > 1024 * 1024 * 1024
    except:
        return False

Configure URL:

python
# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('health/', views.health_check, name='health_check'),
    # Other URLs...
]

Automated Deployment Script

Create a deployment script:

bash
#!/bin/bash
# deploy.sh

set -e  # Exit on error

echo "Starting deployment..."

# Pull latest code
git pull origin main

# Activate virtual environment
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt

# Run migrations
python manage.py migrate --noinput

# Collect static files
python manage.py collectstatic --noinput

# Restart services
sudo systemctl restart myproject
sudo systemctl restart nginx

echo "Deployment completed!"

Backup Strategy

Create a database backup script:

bash
#!/bin/bash
# backup.sh

# Database backup
DATE=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/var/backups/myproject"
mkdir -p $BACKUP_DIR

# PostgreSQL backup
pg_dump myproject > $BACKUP_DIR/db_backup_$DATE.sql

# Compress backup
gzip $BACKUP_DIR/db_backup_$DATE.sql

# Delete backups older than 7 days
find $BACKUP_DIR -name "db_backup_*.sql.gz" -mtime +7 -delete

# Media file backup (if stored locally)
tar -czf $BACKUP_DIR/media_backup_$DATE.tar.gz /var/www/myproject/media/

# Delete media file backups older than 7 days
find $BACKUP_DIR -name "media_backup_*.tar.gz" -mtime +7 -delete

Summary

Key points of Django deployment solutions:

  1. ✅ Nginx + Gunicorn is the classic combination for traditional deployment
  2. ✅ Cloud platform deployment provides more convenient deployment methods
  3. ✅ Use CDN to accelerate static file access
  4. ✅ Implement monitoring and health checks to ensure application stability
  5. ✅ Establish automated deployment and backup strategies

The choice of deployment solution depends on project requirements, team skills, and budget considerations. Regardless of which solution is chosen, the security, stability, and scalability of the application must be ensured.

Through this chapter, you have mastered the complete process of Django production environment configuration and deployment. In actual projects, it is recommended to adjust the configuration according to specific situations and continuously optimize the deployment process.


🎉 Django In-depth Series Course

Congratulations on completing the entire Django In-depth Series Course! Through this course, you have mastered:

Core Skills

  • ✅ Django MTV architecture and project structure
  • ✅ Model design and database operations
  • ✅ View development and URL routing
  • ✅ Template system and form processing
  • ✅ User authentication and permission management

Advanced Features

  • ✅ Admin backend customization
  • ✅ RESTful API development
  • ✅ Test-driven development
  • ✅ Performance optimization techniques
  • ✅ Production environment deployment

Practical Project Experience

  • ✅ Complete blog system development
  • ✅ Front-end and back-end separation architecture
  • ✅ Continuous integration and automated deployment
  • ✅ Monitoring and maintenance strategies

Next Steps

  1. Practice Projects: Try developing a complete personal project
  2. Advanced Learning: Explore advanced features like Django Channels and Celery
  3. Community Participation: Join the Django community and participate in open source projects
  4. Continuous Learning: Follow the latest Django versions and best practices

Thank you for learning this course! Wishing you success in your Django development journey!

Table of Contents

Return to Course Outline

Released under the [BY-NC-ND License](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.en).