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: wwwyour-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

@Kazykan
Copy link

Kazykan commented Oct 20, 2023

3 дня пытался задеплоить на сервер. Только сегодня с помощью этой инструкции сделал это!!!

@FaridLU
Copy link
Author

FaridLU commented Oct 20, 2023

3 дня пытался задеплоить на сервер. Только сегодня с помощью этой инструкции сделал это!!!

Hi @Kazykan, the translation of your message is: "I tried to deploy to the server for 3 days. Only today, with the help of this instruction, I managed to do it!!!".

I am really glad to hear that the instruction was useful for you. 😅

@Roland-Szucs
Copy link

Hi @FaridLU ,

Really great tutorial and covers much more than the original Digital Ocean post. I am beginner in deployment especially on Ubuntu. When I see django/Apache/gunicorn stack to be deployed they change also onwerships and permissions to project directories, database directories adn set owners to WWW-DATA. If we use nginx it is not necessary?

@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