Skip to content

Instantly share code, notes, and snippets.

@FaridLU
Last active January 23, 2026 20:28
Show Gist options
  • Select an option

  • Save FaridLU/377f6adf3cdfd748d430f42e393417f7 to your computer and use it in GitHub Desktop.

Select an option

Save FaridLU/377f6adf3cdfd748d430f42e393417f7 to your computer and use it in GitHub Desktop.
Comprehensive Guide to Deploying Django on Ubuntu (18.04, 20.04, 22.04)

πŸš€ Deploy Django on Ubuntu with Gunicorn, Nginx, Celery & SSL

The Complete Production Deployment Guide for Django Applications

Ubuntu Django Python License


πŸ“– Overview

This comprehensive guide walks you through deploying a production-ready Django application on Ubuntu. You'll learn to configure Gunicorn as the application server, Nginx as a reverse proxy, Celery for background tasks, Redis as a message broker, PostgreSQL as the database, and secure everything with a free SSL certificate from Let's Encrypt.

πŸ—οΈ What You'll Build

                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚      INTERNET (WWW)       β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚ HTTPS (443)
                                  β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚    NGINX (Reverse Proxy)  β”‚
                    β”‚  SSL + Static Files       β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                  β”‚ Unix Socket
                                  β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  GUNICORN (WSGI Server)   β”‚
                    β”‚    Django Application     β”‚
                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                           β”‚             β”‚
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜             └────────────┐
              β–Ό                                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        POSTGRESQL         β”‚         β”‚      CELERY + REDIS       β”‚
β”‚         Database          β”‚         β”‚     Background Tasks      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“‘ Table of Contents


πŸ“‹ Prerequisites

Before starting, ensure you have:

  • πŸ–₯️ A Ubuntu server (18.04, 20.04, 22.04, or 24.04 LTS)
  • πŸ” Root or sudo access to the server
  • 🐍 A Django project ready for deployment
  • πŸ’» Basic familiarity with Linux command line
  • 🌐 A domain name (for SSL setup - optional for initial deployment)

⏱️ Estimated Time: 45-60 minutes for complete setup


πŸ–₯️ Part 1: Initial Server Setup

1.1 Create Your Server

Create a new Ubuntu server on your preferred cloud provider:

Choose a location closest to your target users for optimal latency.

1.2 Connect to Your Server

ssh root@your-server-ip-address

1.3 Create a Non-Root User

πŸ›‘οΈ Security Best Practice: Never run applications as root. Create a dedicated user for deployment.

# Create new user
adduser ubuntu

# Grant sudo privileges
usermod -aG sudo ubuntu

1.4 Configure Firewall

# Allow SSH connections
ufw allow OpenSSH

# Enable firewall
ufw enable

# Verify status
ufw status

1.5 Switch to New User

# From now on, connect as the new user
ssh ubuntu@your-server-ip-address

# Run commands with sudo when needed
sudo your-command

βœ… Checkpoint: You should now be able to SSH into your server as the new user.


πŸ”‘ Part 2: SSH Keys & Environment Configuration

2.1 Generate SSH Key for Git

# Generate SSH key
ssh-keygen -t ed25519 -C "your-email@example.com"

# Display public key (copy this)
cat ~/.ssh/id_ed25519.pub

πŸ’‘ Note: If your system doesn't support Ed25519, use RSA instead:

ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

2.2 Add SSH Key to GitHub/GitLab

  1. πŸ“‹ Copy the public key from the terminal
  2. πŸ”— Navigate to your Git provider's SSH settings:
  3. βž• Click "New SSH Key" and paste your key

2.3 Clone Your Project

# Clone repository
git clone git@github.com:username/your-project.git

# Navigate to project directory
cd your-project

2.4 Install System Dependencies

# Update package list
sudo apt update

# Install required packages
sudo apt install -y \
    python3-venv \
    python3-dev \
    libpq-dev \
    postgresql \
    postgresql-contrib \
    nginx \
    curl \
    build-essential \
    libssl-dev \
    libffi-dev

2.5 Set Up Python Virtual Environment

# Create virtual environment
python3 -m venv venv

# Activate virtual environment
source venv/bin/activate

# Upgrade pip
pip install --upgrade pip

# Install Gunicorn and PostgreSQL adapter
pip install gunicorn psycopg2-binary wheel

# Install project dependencies
pip install -r requirements.txt

2.6 Configure PostgreSQL Database 🐘

# Access PostgreSQL
sudo -u postgres psql

Run the following SQL commands:

-- Create database
CREATE DATABASE your_db_name;

-- Create user with secure password
CREATE USER your_db_user WITH PASSWORD 'your_secure_password';

-- Configure user settings
ALTER ROLE your_db_user SET client_encoding TO 'utf8';
ALTER ROLE your_db_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE your_db_user SET timezone TO 'UTC';

-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE your_db_name TO your_db_user;

-- Exit PostgreSQL
\q

2.7 Configure Django Settings

Update your settings.py (or use environment variables):

# Database configuration
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'your_db_name',
        'USER': 'your_db_user',
        'PASSWORD': 'your_secure_password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

# Allowed hosts
ALLOWED_HOSTS = ['your-server-ip', 'yourdomain.com', 'www.yourdomain.com']

# Static files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static'

# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

πŸ” Security Tip: Use environment variables for sensitive data:

import os
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')

2.8 Initialize Django Application

# Apply migrations
python manage.py migrate

# Collect static files
python manage.py collectstatic --noinput

# Create superuser (optional)
python manage.py createsuperuser

2.9 Test Development Server

# Allow port 8000 temporarily
sudo ufw allow 8000

# Run development server
python manage.py runserver 0.0.0.0:8000

🌐 Visit http://your-server-ip:8000 to verify the application works.

2.10 Test Gunicorn

# Test Gunicorn (replace 'yourproject' with your project name)
gunicorn --bind 0.0.0.0:8000 yourproject.wsgi:application

Press Ctrl+C to stop, then deactivate the virtual environment:

deactivate

βœ… Checkpoint: Django app runs successfully with Gunicorn.


⚑ Part 3: Gunicorn & Nginx Setup

3.1 Create Gunicorn Socket File

sudo nano /etc/systemd/system/gunicorn.socket

Add the following content:

[Unit]
Description=Gunicorn socket for Django

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

3.2 Create Gunicorn Service File

sudo nano /etc/systemd/system/gunicorn.service

Add the following content (update paths as needed):

[Unit]
Description=Gunicorn daemon for Django
Requires=gunicorn.socket
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/your-project
ExecStart=/home/ubuntu/your-project/venv/bin/gunicorn \
          --access-logfile - \
          --error-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          yourproject.wsgi:application

[Install]
WantedBy=multi-user.target

πŸ’‘ Performance Tip: Set workers to (2 Γ— CPU cores) + 1 for optimal performance.

3.3 Start and Enable Gunicorn

# Start Gunicorn socket
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket

# Verify socket is running
sudo systemctl status gunicorn.socket

# Check socket file exists
file /run/gunicorn.sock

3.4 Configure Nginx πŸ”§

Create a new Nginx configuration:

sudo nano /etc/nginx/sites-available/yourproject

Add the following configuration:

server {
    listen 80;
    server_name your-server-ip yourdomain.com www.yourdomain.com;

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

    # Favicon
    location = /favicon.ico {
        access_log off;
        log_not_found off;
    }

    # Static files
    location /static/ {
        alias /home/ubuntu/your-project/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Media files
    location /media/ {
        alias /home/ubuntu/your-project/media/;
        expires 7d;
    }

    # Proxy to Gunicorn
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
        proxy_send_timeout 300;
    }

    # Max upload size
    client_max_body_size 10M;
}

3.5 Enable the Site

# Create symbolic link
sudo ln -s /etc/nginx/sites-available/yourproject /etc/nginx/sites-enabled/

# Test configuration
sudo nginx -t

# Restart Nginx
sudo systemctl restart nginx

3.6 Update Firewall Rules

# Remove temporary port 8000 rule
sudo ufw delete allow 8000

# Allow Nginx
sudo ufw allow 'Nginx Full'

3.7 Verify Deployment

πŸŽ‰ Visit http://your-server-ip in your browser. Your Django application should be running!

βœ… Checkpoint: Django app is accessible via Nginx.


πŸ“¦ Part 4: Celery, Redis & Supervisor

⏭️ Skip this section if your application doesn't use background tasks.

4.1 Install Redis and Supervisor

# Install Redis
sudo apt install -y redis-server

# Install Supervisor
sudo apt install -y supervisor

# Verify Redis is running
sudo systemctl status redis

4.2 Create Celery Worker Configuration πŸ₯¬

sudo nano /etc/supervisor/conf.d/celery_worker.conf

Add the following content:

[program:celeryworker]
command=/home/ubuntu/your-project/venv/bin/celery -A yourproject worker --loglevel=INFO
directory=/home/ubuntu/your-project
user=ubuntu
numprocs=1
stdout_logfile=/var/log/celery/worker.log
stderr_logfile=/var/log/celery/worker.log
autostart=true
autorestart=true
startsecs=10
stopwaitsecs=600
killasgroup=true
priority=998

4.3 Create Celery Beat Configuration (Optional) ⏰

For scheduled tasks:

sudo nano /etc/supervisor/conf.d/celery_beat.conf

Add the following content:

[program:celerybeat]
command=/home/ubuntu/your-project/venv/bin/celery -A yourproject beat --loglevel=INFO
directory=/home/ubuntu/your-project
user=ubuntu
numprocs=1
stdout_logfile=/var/log/celery/beat.log
stderr_logfile=/var/log/celery/beat.log
autostart=true
autorestart=true
startsecs=10
priority=999

4.4 Create Log Directory and Start Services

# Create log directory
sudo mkdir -p /var/log/celery
sudo chown ubuntu:ubuntu /var/log/celery

# Update Supervisor
sudo supervisorctl reread
sudo supervisorctl update

# Start services
sudo supervisorctl start all

# Check status
sudo supervisorctl status

4.5 Useful Supervisor Commands πŸ“

# Restart all services
sudo supervisorctl restart all

# View worker logs
sudo tail -f /var/log/celery/worker.log

# View beat logs
sudo tail -f /var/log/celery/beat.log

βœ… Checkpoint: Celery workers are running in the background.


🌐 Part 5: Domain & SSL Certificate

5.1 Point Domain to Server πŸ”—

Configure your domain's DNS settings:

  • A Record: @ β†’ your-server-ip
  • A Record: www β†’ your-server-ip

⏳ Note: DNS propagation can take up to 48 hours, but usually completes within minutes.

5.2 Update Nginx Configuration

sudo nano /etc/nginx/sites-available/yourproject

Update the server_name directive:

server_name yourdomain.com www.yourdomain.com;

5.3 Update Django Settings

ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

5.4 Reload Nginx

sudo nginx -t
sudo systemctl reload nginx

5.5 Install Certbot πŸ”’

# Install Certbot
sudo snap install --classic certbot

# Create symbolic link
sudo ln -s /snap/bin/certbot /usr/bin/certbot

5.6 Obtain SSL Certificate

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

Follow the prompts:

  1. πŸ“§ Enter your email address
  2. βœ… Agree to terms of service (Y)
  3. πŸ“° Choose whether to share email with EFF

5.7 Verify Auto-Renewal

# Test renewal process
sudo certbot renew --dry-run

# Check renewal timer
sudo systemctl status snap.certbot.renew.timer

πŸŽ‰ Your site is now accessible via https://yourdomain.com!

βœ… Checkpoint: SSL certificate installed and auto-renewal configured.


πŸ”§ Troubleshooting

Common Issues and Solutions

❌ 502 Bad Gateway
  1. Check if Gunicorn is running:

    sudo systemctl status gunicorn
  2. Check Gunicorn logs:

    sudo journalctl -u gunicorn
  3. Verify socket file exists:

    file /run/gunicorn.sock
  4. Restart services:

    sudo systemctl daemon-reload
    sudo systemctl restart gunicorn
    sudo systemctl restart nginx
πŸ–ΌοΈ Static Files Not Loading
  1. Verify STATIC_ROOT in settings.py
  2. Run python manage.py collectstatic
  3. Check Nginx configuration paths
  4. Verify file permissions:
    sudo chown -R ubuntu:www-data /home/ubuntu/your-project/static
🚫 Permission Denied Errors
  1. Check file ownership:

    ls -la /home/ubuntu/your-project
  2. Fix permissions:

    sudo chown -R ubuntu:www-data /home/ubuntu/your-project
    sudo chmod -R 755 /home/ubuntu/your-project
🐘 Database Connection Failed
  1. Verify PostgreSQL is running:

    sudo systemctl status postgresql
  2. Test database connection:

    sudo -u postgres psql -c "\l"
  3. Check credentials in settings.py

πŸ₯¬ Celery Tasks Not Running
  1. Check Supervisor status:

    sudo supervisorctl status
  2. View Celery logs:

    sudo tail -f /var/log/celery/worker.log
  3. Restart Celery:

    sudo supervisorctl restart celeryworker

πŸ“š Quick Reference

πŸ”„ Service Management

Action Command
πŸ”„ Restart Gunicorn sudo systemctl restart gunicorn
πŸ”„ Restart Nginx sudo systemctl restart nginx
πŸ”„ Restart Celery sudo supervisorctl restart all
πŸ“‹ View Gunicorn logs sudo journalctl -u gunicorn -f
πŸ“‹ View Nginx error log sudo tail -f /var/log/nginx/error.log
πŸ“‹ View Celery logs sudo tail -f /var/log/celery/worker.log

βœ… Deployment Checklist

  • πŸ–₯️ Server created and SSH access configured
  • πŸ‘€ Non-root user created with sudo privileges
  • πŸ”₯ Firewall configured (UFW)
  • πŸ”‘ Git SSH key added to repository
  • πŸ“¦ Project cloned and dependencies installed
  • 🐘 PostgreSQL database configured
  • βš™οΈ Django settings updated for production
  • πŸ“ Static files collected
  • πŸ¦„ Gunicorn socket and service configured
  • πŸ”€ Nginx configured as reverse proxy
  • πŸ₯¬ Celery and Supervisor configured (if needed)
  • 🌐 Domain pointed to server
  • πŸ”’ SSL certificate installed
  • πŸ”„ Auto-renewal verified

πŸš€ Update Deployment

When you push new code:

cd ~/your-project
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
python manage.py migrate
python manage.py collectstatic --noinput
sudo systemctl restart gunicorn
sudo supervisorctl restart all  # If using Celery
deactivate

🀝 Contributing

Found an issue or have a suggestion? Open an issue or submit a pull request.


πŸ“„ License

This guide is released under the MIT License.


⭐ If this guide helped you, please give it a star! ⭐

Made with ❀️ for the Django community

DigitalOcean Referral Badge

@gunjanak
Copy link

/etc/systemd/system/gunicorn.socket:1: Assignment outside of section. Ignoring.
Help me I am getting this error

@FaridLU
Copy link
Author

FaridLU commented Feb 14, 2024

/etc/systemd/system/gunicorn.socket:1: Assignment outside of section. Ignoring. Help me I am getting this error

@gunjanak, can you please show me the gunicorn.socket file that you wrote? I think you've misaligned the config of that file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment