Deploying Django to Production: Complete Server Setup Guide
# Deploying Django to Production
Taking your Django app from development to production requires careful setup. Here's our complete deployment guide.
## Server Requirements
- Ubuntu 22.04 LTS (or similar)
- 2+ GB RAM
- Python 3.11+
- PostgreSQL 15+
- Nginx
- SSL certificate (Let's Encrypt)
## Initial Server Setup
```bash
# Update system
sudo apt update && sudo apt upgrade -y
# Install dependencies
sudo apt install -y python3-pip python3-venv python3-dev \
postgresql postgresql-contrib \
nginx certbot python3-certbot-nginx \
supervisor git curl
# Create app user
sudo useradd -m -s /bin/bash djzen
sudo usermod -aG sudo djzen
```
## PostgreSQL Setup
```bash
# Create database and user
sudo -u postgres psql
CREATE DATABASE djzen;
CREATE USER djzen_user WITH PASSWORD 'secure_password_here';
ALTER ROLE djzen_user SET client_encoding TO 'utf8';
ALTER ROLE djzen_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE djzen_user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE djzen TO djzen_user;
\q
```
## Application Setup
```bash
# Switch to app user
sudo su - djzen
# Clone repository
git clone https://github.com/yourusername/djzen.git
cd djzen
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
pip install gunicorn psycopg2-binary
# Create environment file
cat > .env << EOF
DEBUG=False
SECRET_KEY=$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())')
DATABASE_URL=postgres://djzen_user:secure_password_here@localhost:5432/djzen
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com
STATIC_ROOT=/var/www/djzen/static
MEDIA_ROOT=/var/www/djzen/media
EOF
# Run migrations
python manage.py migrate
# Collect static files
python manage.py collectstatic --noinput
# Create superuser
python manage.py createsuperuser
```
## Gunicorn Configuration
```bash
# /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn daemon for DjangoZen
After=network.target
[Service]
User=djzen
Group=www-data
WorkingDirectory=/home/djzen/djzen
ExecStart=/home/djzen/djzen/venv/bin/gunicorn \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log \
--workers 3 \
--bind unix:/run/gunicorn/gunicorn.sock \
--timeout 120 \
djzen.wsgi:application
[Install]
WantedBy=multi-user.target
```
```bash
# Create directories
sudo mkdir -p /run/gunicorn /var/log/gunicorn
sudo chown djzen:www-data /run/gunicorn /var/log/gunicorn
# Enable and start
sudo systemctl enable gunicorn
sudo systemctl start gunicorn
```
## Nginx Configuration
```nginx
# /etc/nginx/sites-available/djzen
upstream gunicorn {
server unix:/run/gunicorn/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
# SSL certificates (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# Security headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Static files
location /static/ {
alias /var/www/djzen/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Media files
location /media/ {
alias /var/www/djzen/media/;
expires 7d;
}
# Application
location / {
proxy_pass http://gunicorn;
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_redirect off;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/html;
}
}
```
```bash
# Enable site
sudo ln -s /etc/nginx/sites-available/djzen /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
```
## SSL Certificate
```bash
# Get certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Auto-renewal (already configured by Certbot)
sudo certbot renew --dry-run
```
## Redis & Celery Setup
```bash
# Install Redis
sudo apt install redis-server
sudo systemctl enable redis-server
# Celery service
# /etc/systemd/system/celery.service
[Unit]
Description=Celery Worker
After=network.target
[Service]
User=djzen
Group=www-data
WorkingDirectory=/home/djzen/djzen
ExecStart=/home/djzen/djzen/venv/bin/celery -A djzen worker -l INFO
[Install]
WantedBy=multi-user.target
# Celery Beat
# /etc/systemd/system/celerybeat.service
[Unit]
Description=Celery Beat Scheduler
After=network.target
[Service]
User=djzen
Group=www-data
WorkingDirectory=/home/djzen/djzen
ExecStart=/home/djzen/djzen/venv/bin/celery -A djzen beat -l INFO
[Install]
WantedBy=multi-user.target
```
## Deployment Script
```bash
#!/bin/bash
# deploy.sh
set -e
echo "Deploying DjangoZen..."
cd /home/djzen/djzen
source venv/bin/activate
# Pull latest code
git pull origin main
# 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 gunicorn
sudo systemctl restart celery
sudo systemctl restart celerybeat
echo "Deployment complete!"
```
## Monitoring
```bash
# Check service status
sudo systemctl status gunicorn
sudo systemctl status nginx
sudo systemctl status celery
# View logs
sudo journalctl -u gunicorn -f
sudo tail -f /var/log/nginx/error.log
sudo tail -f /var/log/gunicorn/error.log
```
## Security Checklist
- [ ] DEBUG = False
- [ ] Strong SECRET_KEY
- [ ] HTTPS enforced
- [ ] Database password secure
- [ ] Firewall configured (ufw)
- [ ] Regular backups
- [ ] Monitoring alerts
- [ ] Security updates enabled
Your Django app is now production-ready!