mirror of
https://github.com/thecyberlearn/hostinger-django-demo.git
synced 2026-08-18 10:12:58 +00:00
- 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>
337 lines
7.6 KiB
Markdown
337 lines
7.6 KiB
Markdown
# 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
|
|
```bash
|
|
ssh root@YOUR_VPS_IP
|
|
apt update && apt upgrade -y
|
|
```
|
|
|
|
### Install Required Packages
|
|
```bash
|
|
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
|
|
```bash
|
|
adduser django --disabled-password --gecos ''
|
|
usermod -aG sudo django
|
|
```
|
|
|
|
### Configure SSH for New User (Optional)
|
|
```bash
|
|
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
|
|
```bash
|
|
mkdir -p /var/www
|
|
chown django:www-data /var/www
|
|
```
|
|
|
|
### Clone Project (as django user)
|
|
```bash
|
|
sudo -u django bash -c "
|
|
cd /var/www
|
|
git clone YOUR_REPO_URL django-app
|
|
cd django-app
|
|
"
|
|
```
|
|
|
|
### Create Virtual Environment
|
|
```bash
|
|
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
|
|
```bash
|
|
sudo -u django bash -c "
|
|
cd /var/www/django-app
|
|
cp .env.example .env
|
|
# Edit .env with production settings
|
|
"
|
|
```
|
|
|
|
**Required .env settings:**
|
|
```env
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
cat > /etc/systemd/system/gunicorn.socket << 'EOF'
|
|
[Unit]
|
|
Description=gunicorn socket
|
|
|
|
[Socket]
|
|
ListenStream=/run/gunicorn.sock
|
|
|
|
[Install]
|
|
WantedBy=sockets.target
|
|
EOF
|
|
```
|
|
|
|
### Create Gunicorn Service
|
|
```bash
|
|
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
|
|
```bash
|
|
systemctl daemon-reload
|
|
systemctl start gunicorn.socket
|
|
systemctl enable gunicorn.socket
|
|
systemctl status gunicorn.socket
|
|
```
|
|
|
|
## 🌐 **Step 5: Nginx Configuration**
|
|
|
|
### Create Nginx Site Config
|
|
```bash
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
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
|
|
```bash
|
|
apt install certbot python3-certbot-nginx -y
|
|
```
|
|
|
|
### Get SSL Certificate
|
|
```bash
|
|
certbot --nginx -d yourdomain.com -d www.yourdomain.com
|
|
```
|
|
|
|
### Update Environment for HTTPS
|
|
```bash
|
|
# In /var/www/django-app/.env
|
|
SECURE_SSL_REDIRECT=True
|
|
```
|
|
|
|
## ✅ **Step 8: Verification**
|
|
|
|
### Test Application
|
|
```bash
|
|
curl -I http://YOUR_VPS_IP # Should return 200 OK
|
|
```
|
|
|
|
### Check Services
|
|
```bash
|
|
systemctl status gunicorn.socket
|
|
systemctl status gunicorn.service
|
|
systemctl status nginx
|
|
```
|
|
|
|
### View Logs
|
|
```bash
|
|
journalctl -u gunicorn.service -f # Django logs
|
|
tail -f /var/log/nginx/error.log # Nginx logs
|
|
```
|
|
|
|
## 🔄 **Deployment Updates**
|
|
|
|
### Update Code
|
|
```bash
|
|
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**:
|
|
```bash
|
|
systemctl status gunicorn.service
|
|
systemctl restart gunicorn.socket
|
|
```
|
|
|
|
### Issue: Static files not loading
|
|
**Cause**: Nginx can't access staticfiles directory
|
|
**Solution**:
|
|
```bash
|
|
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**:
|
|
```bash
|
|
chown -R django:www-data /var/www/django-app
|
|
```
|
|
|
|
### Issue: ModuleNotFoundError
|
|
**Cause**: Virtual environment not recreated after moving files
|
|
**Solution**:
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
# 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. |