hostinger-django-demo/PRODUCTION_DEPLOYMENT.md
thecyberlearn f721b0a87e Add comprehensive production deployment system
- Complete deployment documentation (PRODUCTION_DEPLOYMENT.md)
- Automated deployment script (production-deploy.sh)
- Systemd service templates (socket + service)
- Production settings template with security best practices
- Nginx configuration template with performance optimizations
- Comprehensive troubleshooting guide
- Quick start guide for fast deployment

Fixes all issues encountered in initial deployment:
- Uses non-root django user for security
- Proper /var/www directory structure
- Unix socket instead of TCP for better performance
- Socket activation with systemd
- Correct virtual environment handling
- Production security headers and settings

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 23:03:30 +05:30

7.6 KiB

Production Django Deployment Guide

A comprehensive, battle-tested guide for deploying Django applications on VPS with proper security and performance.

🎯 Overview

This guide follows Django best practices and avoids common pitfalls we encountered:

  • Avoid: Running as root, wrong directories, TCP sockets, hardcoded paths
  • Use: Non-root user, /var/www/, Unix sockets, proper systemd configuration

📋 Prerequisites

  • Ubuntu 20.04+ VPS with root access
  • Domain name (optional, can use IP address)
  • Git repository with Django project

🚀 Step 1: VPS Initial Setup

Connect and Update System

ssh root@YOUR_VPS_IP
apt update && apt upgrade -y

Install Required Packages

apt install -y python3 python3-pip python3-venv python3-dev \
               nginx postgresql postgresql-contrib libpq-dev \
               build-essential curl git ufw

Create Non-Root User

adduser django --disabled-password --gecos ''
usermod -aG sudo django

Configure SSH for New User (Optional)

mkdir -p /home/django/.ssh
cp /root/.ssh/authorized_keys /home/django/.ssh/
chown -R django:django /home/django/.ssh
chmod 700 /home/django/.ssh
chmod 600 /home/django/.ssh/authorized_keys

🗂️ Step 2: Project Setup

Create Project Directory

mkdir -p /var/www
chown django:www-data /var/www

Clone Project (as django user)

sudo -u django bash -c "
cd /var/www
git clone YOUR_REPO_URL django-app
cd django-app
"

Create Virtual Environment

sudo -u django bash -c "
cd /var/www/django-app
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
"

⚙️ Step 3: Django Configuration

Environment Variables

sudo -u django bash -c "
cd /var/www/django-app
cp .env.example .env
# Edit .env with production settings
"

Required .env settings:

SECRET_KEY=your-super-secret-key-generate-new-one
DEBUG=False
ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,YOUR_VPS_IP
DATABASE_URL=postgresql://dbuser:dbpassword@localhost/dbname
SECURE_SSL_REDIRECT=False  # Set True after SSL setup

Run Django Setup

sudo -u django bash -c "
cd /var/www/django-app
source venv/bin/activate
python manage.py collectstatic --noinput
python manage.py migrate
python manage.py createsuperuser
"

🔧 Step 4: Gunicorn with systemd

Create Gunicorn Socket

cat > /etc/systemd/system/gunicorn.socket << 'EOF'
[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target
EOF

Create Gunicorn Service

cat > /etc/systemd/system/gunicorn.service << 'EOF'
[Unit]
Description=Gunicorn daemon for Django app
Requires=gunicorn.socket
After=network.target

[Service]
User=django
Group=www-data
WorkingDirectory=/var/www/django-app
Environment=DJANGO_SETTINGS_MODULE=PROJECT_NAME.settings
EnvironmentFile=/var/www/django-app/.env
ExecStart=/var/www/django-app/venv/bin/gunicorn \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          PROJECT_NAME.wsgi:application
Restart=always

[Install]
WantedBy=multi-user.target
EOF

⚠️ Replace PROJECT_NAME with your actual Django project name!

Start Services

systemctl daemon-reload
systemctl start gunicorn.socket
systemctl enable gunicorn.socket
systemctl status gunicorn.socket

🌐 Step 5: Nginx Configuration

Create Nginx Site Config

cat > /etc/nginx/sites-available/django-app << 'EOF'
server {
    listen 80;
    server_name YOUR_DOMAIN.com www.YOUR_DOMAIN.com YOUR_VPS_IP;

    # 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;
    add_header Referrer-Policy "no-referrer-when-downgrade" 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 application/xml+rss text/javascript;

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }

    location /static/ {
        alias /var/www/django-app/staticfiles/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    location /media/ {
        alias /var/www/django-app/media/;
        expires 1y;
        add_header Cache-Control "public";
    }

    # Block access to sensitive files
    location ~* /\.(?!well-known\/) {
        deny all;
    }

    location ~* /(requirements\.txt|\.env|deploy/|\.git/) {
        deny all;
    }
}
EOF

Enable Site

ln -s /etc/nginx/sites-available/django-app /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t
systemctl restart nginx

🔒 Step 6: Security & Firewall

Configure UFW Firewall

ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 'Nginx Full'
ufw --force enable
ufw status

🔐 Step 7: SSL Certificate (Optional)

Install Certbot

apt install certbot python3-certbot-nginx -y

Get SSL Certificate

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

Update Environment for HTTPS

# In /var/www/django-app/.env
SECURE_SSL_REDIRECT=True

Step 8: Verification

Test Application

curl -I http://YOUR_VPS_IP  # Should return 200 OK

Check Services

systemctl status gunicorn.socket
systemctl status gunicorn.service  
systemctl status nginx

View Logs

journalctl -u gunicorn.service -f  # Django logs
tail -f /var/log/nginx/error.log   # Nginx logs

🔄 Deployment Updates

Update Code

sudo -u django bash -c "
cd /var/www/django-app
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput
"
systemctl restart gunicorn.service

🚨 Common Issues & Solutions

Issue: 502 Bad Gateway

Cause: Gunicorn not running or socket issues Solution:

systemctl status gunicorn.service
systemctl restart gunicorn.socket

Issue: Static files not loading

Cause: Nginx can't access staticfiles directory Solution:

sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py collectstatic --noinput"

Issue: Permission denied

Cause: Wrong file ownership Solution:

chown -R django:www-data /var/www/django-app

Issue: ModuleNotFoundError

Cause: Virtual environment not recreated after moving files Solution:

sudo -u django bash -c "cd /var/www/django-app && rm -rf venv && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"

📝 Quick Commands Reference

# Service management
systemctl restart gunicorn.service
systemctl restart nginx
systemctl status gunicorn.service

# View logs
journalctl -u gunicorn.service -f
tail -f /var/log/nginx/access.log

# Django management
sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py COMMAND"

🎯 Key Differences from Our Initial Approach

  1. User: Use django user instead of root
  2. Location: Use /var/www/django-app instead of /root/django-demo
  3. Socket: Use Unix socket instead of TCP
  4. Virtual Environment: Always recreate in target location
  5. Systemd: Use socket activation with proper service file
  6. Security: Proper file permissions and firewall rules

This configuration is production-ready, secure, and follows Django best practices.