Python/Django Guide

    Django + Gunicorn + Nginx

    Deploy a production-ready Django application using Gunicorn as the WSGI server and Nginx as a reverse proxy on RamNode VPS. This battle-tested stack powers thousands of production applications worldwide.

    Ubuntu 22.04/24.04
    Django + PostgreSQL
    ⏱️ 45-60 minutes

    Why This Stack?

    This architecture separates concerns for optimal performance and security:

    Django

    Handles your application logic, URL routing, templates, and ORM.

    Gunicorn

    Manages multiple worker processes for concurrent request handling.

    Nginx

    Serves static files efficiently and acts as a reverse proxy.

    Prerequisites & VPS Selection

    Small Apps

    • • 1GB RAM
    • • 1 vCPU
    • • Simple Django sites

    Recommended

    • • 2GB RAM
    • • 2 vCPU
    • • Django + PostgreSQL

    High Traffic

    • • 4GB+ RAM
    • • 4 vCPU
    • • Multiple workers

    📋 Requirements: Ubuntu 22.04/24.04 LTS, root or sudo access, a domain name pointed to your VPS IP, and your Django project ready for deployment.

    2

    Initial Server Setup

    Connect to your RamNode VPS and update the system:

    Connect and update
    ssh root@your_server_ip
    apt update && apt upgrade -y

    Create a dedicated user for running your Django application (security best practice):

    Create application user
    adduser django
    usermod -aG sudo django
    su - django
    3

    Install Required Packages

    Install Python, pip, Nginx, and other essential packages:

    Install dependencies
    sudo apt install python3 python3-pip python3-venv nginx curl git -y

    Verify the installations:

    Verify installations
    python3 --version
    nginx -v
    4

    Set Up Your Django Project

    Create a directory structure and set up a Python virtual environment:

    Create project directory
    mkdir -p ~/apps/myproject
    cd ~/apps/myproject
    python3 -m venv venv
    source venv/bin/activate

    Clone your existing project or create a new one:

    Clone or create project
    # Clone existing project
    git clone https://github.com/yourusername/your-django-project.git .
    
    # Or create a new project
    pip install django
    django-admin startproject myproject .

    Install project dependencies:

    Install dependencies
    pip install -r requirements.txt
    pip install gunicorn
    5

    Configure Django for Production

    Update your Django settings for production:

    myproject/settings.py
    import os
    from pathlib import Path
    
    BASE_DIR = Path(__file__).resolve().parent.parent
    
    # Security settings
    DEBUG = False
    SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
    ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com', 'your_server_ip']
    
    # Static files configuration
    STATIC_URL = '/static/'
    STATIC_ROOT = BASE_DIR / 'staticfiles'
    
    # Media files configuration
    MEDIA_URL = '/media/'
    MEDIA_ROOT = BASE_DIR / 'media'
    
    # Security headers
    SECURE_BROWSER_XSS_FILTER = True
    SECURE_CONTENT_TYPE_NOSNIFF = True
    X_FRAME_OPTIONS = 'DENY'
    CSRF_COOKIE_SECURE = True
    SESSION_COOKIE_SECURE = True

    🔐 Security Note: Never hardcode your SECRET_KEY in production. Use environment variables. Generate a new key with:

    Generate secret key
    python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"

    Collect static files and run migrations:

    Prepare for production
    python manage.py collectstatic --noinput
    python manage.py migrate
    6

    Configure Gunicorn

    Test that Gunicorn can serve your application:

    Test Gunicorn
    cd ~/apps/myproject
    source venv/bin/activate
    gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application

    If this works, press Ctrl+C to stop. Now create a Gunicorn configuration file:

    ~/apps/myproject/gunicorn.conf.py
    import multiprocessing
    
    # Binding
    bind = "unix:/run/gunicorn/myproject.sock"
    
    # Worker processes
    workers = multiprocessing.cpu_count() * 2 + 1
    worker_class = "sync"
    worker_connections = 1000
    timeout = 30
    keepalive = 2
    
    # Logging
    accesslog = "/var/log/gunicorn/access.log"
    errorlog = "/var/log/gunicorn/error.log"
    loglevel = "info"
    
    # Process naming
    proc_name = "myproject"
    
    # Security
    limit_request_line = 4094
    limit_request_fields = 100
    limit_request_field_size = 8190

    Create the necessary directories:

    Create directories
    sudo mkdir -p /run/gunicorn /var/log/gunicorn
    sudo chown django:django /run/gunicorn /var/log/gunicorn
    7

    Create Systemd Services

    Create a systemd socket file for socket activation:

    Create socket file
    sudo nano /etc/systemd/system/gunicorn.socket
    /etc/systemd/system/gunicorn.socket
    [Unit]
    Description=Gunicorn socket for myproject
    
    [Socket]
    ListenStream=/run/gunicorn/myproject.sock
    SocketUser=www-data
    SocketGroup=www-data
    SocketMode=0660
    
    [Install]
    WantedBy=sockets.target

    Create the Gunicorn service file:

    Create service file
    sudo nano /etc/systemd/system/gunicorn.service
    /etc/systemd/system/gunicorn.service
    [Unit]
    Description=Gunicorn daemon for myproject
    Requires=gunicorn.socket
    After=network.target
    
    [Service]
    Type=notify
    User=django
    Group=django
    RuntimeDirectory=gunicorn
    WorkingDirectory=/home/django/apps/myproject
    Environment="DJANGO_SECRET_KEY=your-secret-key-here"
    Environment="DJANGO_SETTINGS_MODULE=myproject.settings"
    ExecStart=/home/django/apps/myproject/venv/bin/gunicorn \
        --config /home/django/apps/myproject/gunicorn.conf.py \
        myproject.wsgi:application
    ExecReload=/bin/kill -s HUP $MAINPID
    KillMode=mixed
    TimeoutStopSec=5
    PrivateTmp=true
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target

    Enable and start the services:

    Start services
    sudo systemctl daemon-reload
    sudo systemctl enable gunicorn.socket gunicorn.service
    sudo systemctl start gunicorn.socket
    sudo systemctl start gunicorn.service

    Verify the services are running:

    Verify services
    sudo systemctl status gunicorn.socket
    sudo systemctl status gunicorn.service
    curl --unix-socket /run/gunicorn/myproject.sock localhost
    8

    Configure Nginx

    Create an Nginx server block for your Django application:

    Create Nginx config
    sudo nano /etc/nginx/sites-available/myproject
    /etc/nginx/sites-available/myproject
    upstream django_app {
        server unix:/run/gunicorn/myproject.sock fail_timeout=0;
    }
    
    server {
        listen 80;
        listen [::]:80;
        server_name yourdomain.com www.yourdomain.com;
    
        # Redirect HTTP to HTTPS (uncomment after SSL setup)
        # return 301 https://$server_name$request_uri;
    
        client_max_body_size 10M;
    
        # Security headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
        # Static files
        location /static/ {
            alias /home/django/apps/myproject/staticfiles/;
            expires 30d;
            add_header Cache-Control "public, immutable";
        }
    
        # Media files
        location /media/ {
            alias /home/django/apps/myproject/media/;
            expires 7d;
            add_header Cache-Control "public";
        }
    
        # Favicon
        location /favicon.ico {
            alias /home/django/apps/myproject/staticfiles/favicon.ico;
            access_log off;
            log_not_found off;
        }
    
        # Proxy to Gunicorn
        location / {
            proxy_set_header Host $http_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;
            proxy_pass http://django_app;
        }
    
        # Deny access to hidden files
        location ~ /\. {
            deny all;
            access_log off;
            log_not_found off;
        }
    }

    Enable the site and test the configuration:

    Enable and test
    sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl restart nginx
    9

    Secure with SSL (Let's Encrypt)

    Install Certbot and obtain a free SSL certificate:

    Install and run Certbot
    sudo apt install certbot python3-certbot-nginx -y
    sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

    Verify auto-renewal is working:

    Test auto-renewal
    sudo certbot renew --dry-run

    💡 Tip: After SSL is configured, update your Django settings to set SECURE_SSL_REDIRECT = True and uncomment the HTTP to HTTPS redirect in your Nginx configuration.

    10

    Configure the Firewall

    Set up UFW (Uncomplicated Firewall) to secure your server:

    Configure UFW
    sudo ufw default deny incoming
    sudo ufw default allow outgoing
    sudo ufw allow ssh
    sudo ufw allow 'Nginx Full'
    sudo ufw enable
    sudo ufw status
    11

    Database Configuration (PostgreSQL)

    For production, PostgreSQL is recommended over SQLite. Install and configure:

    Install PostgreSQL
    sudo apt install postgresql postgresql-contrib libpq-dev -y
    sudo systemctl start postgresql
    sudo systemctl enable postgresql

    Create a database and user for your Django application:

    Create database
    sudo -u postgres psql
    
    CREATE DATABASE myproject;
    CREATE USER djangouser WITH PASSWORD 'secure_password_here';
    ALTER ROLE djangouser SET client_encoding TO 'utf8';
    ALTER ROLE djangouser SET default_transaction_isolation TO 'read committed';
    ALTER ROLE djangouser SET timezone TO 'UTC';
    GRANT ALL PRIVILEGES ON DATABASE myproject TO djangouser;
    \q

    Install the PostgreSQL adapter and update Django settings:

    Install psycopg2
    source ~/apps/myproject/venv/bin/activate
    pip install psycopg2-binary
    settings.py - Database configuration
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.postgresql',
            'NAME': 'myproject',
            'USER': 'djangouser',
            'PASSWORD': os.environ.get('DB_PASSWORD'),
            'HOST': 'localhost',
            'PORT': '5432',
        }
    }
    12

    Monitoring and Logging

    Set up log rotation to prevent logs from consuming disk space:

    Create logrotate config
    sudo nano /etc/logrotate.d/gunicorn
    /etc/logrotate.d/gunicorn
    /var/log/gunicorn/*.log {
        daily
        missingok
        rotate 14
        compress
        delaycompress
        notifempty
        create 0640 django django
        sharedscripts
        postrotate
            systemctl reload gunicorn > /dev/null 2>&1 || true
        endscript
    }

    Useful commands for monitoring your deployment:

    Gunicorn Logs

    View logs
    sudo tail -f /var/log/gunicorn/error.log
    sudo tail -f /var/log/gunicorn/access.log

    Nginx Logs

    View logs
    sudo tail -f /var/log/nginx/error.log
    sudo tail -f /var/log/nginx/access.log
    Service status and Django logs
    # Check service status
    sudo systemctl status gunicorn nginx
    
    # View Django application logs
    journalctl -u gunicorn -f
    13

    Maintenance and Updates

    Deploying updates to your Django application:

    Deploy updates
    cd ~/apps/myproject
    source venv/bin/activate
    git pull origin main
    pip install -r requirements.txt
    python manage.py migrate
    python manage.py collectstatic --noinput
    sudo systemctl restart gunicorn

    For graceful reloads without dropping connections (zero-downtime):

    Zero-downtime reload
    sudo systemctl reload gunicorn

    Troubleshooting Common Issues

    IssueSolution
    502 Bad GatewayCheck Gunicorn is running: sudo systemctl status gunicorn. Verify socket permissions.
    Static files 404Run collectstatic, verify STATIC_ROOT path matches Nginx alias.
    Permission deniedCheck ownership: chown -R django:django ~/apps/myproject
    Socket not foundStart socket: sudo systemctl start gunicorn.socket
    Database errorsVerify PostgreSQL is running, check credentials and migrations.

    Performance Tuning

    Optimize Gunicorn workers based on your RamNode VPS specifications. The formula workers = (2 × CPU cores) + 1 is a good starting point.

    For memory-constrained environments, reduce workers and use --max-requests to prevent memory leaks:

    Performance-tuned gunicorn.conf.py
    # For a 2-core VPS with 4GB RAM
    workers = 5
    max_requests = 1000
    max_requests_jitter = 50

    Enable Nginx gzip compression for better performance:

    Add to nginx.conf http block
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json
               application/javascript application/xml+rss
               application/atom+xml image/svg+xml;

    Deployment Complete!

    You now have a production-ready Django deployment on your RamNode VPS. This setup provides a solid foundation that can handle significant traffic while remaining maintainable and secure.

    As your application grows, consider adding Redis for caching and Celery for background task processing. For additional resources, visit the official documentation for Django, Gunicorn, and Nginx.

    Need More Compute Power?

    RamNode offers easy VPS scaling to meet your growing needs.

    Security Checklist

    • DEBUG = False - Never run with DEBUG enabled in production
    • Use environment variables - Never hardcode secrets in settings.py
    • Enable HTTPS - Use Let's Encrypt for free SSL certificates
    • Configure firewall - Only allow necessary ports (22, 80, 443)
    • Keep software updated - Regular apt update && apt upgrade
    • Use separate database user - Don't use postgres superuser for Django
    • Enable security headers - X-Frame-Options, X-Content-Type-Options, etc.