Back to Blog

Deploying Django to Production: Complete Server Setup Guide

admin
November 21, 2025 4 min read
11 views
Step-by-step guide to deploying Django with Gunicorn, Nginx, PostgreSQL, and SSL on a Linux server.

# 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!

Comments (0)

Please login to leave a comment.

No comments yet. Be the first to comment!