Language:

Search

How to Deploy a Django Project on Ubuntu Using Nginx and Gunicorn

How to Deploy a Django Project on Ubuntu Using Nginx and Gunicorn

Setting up your Django app for production might seem complex, but it doesn’t have to be. This step-by-step guide walks you through deploying a Django web application on Ubuntu, using Nginx as a reverse proxy, Gunicorn as the application server, and either MySQL or PostgreSQL as the database backend.

Whether you're a developer, DevOps engineer, or startup founder, this tutorial will walk you through each step with clarity and confidence.

Prerequisites

Before diving in, make sure you have the following:

  • A cloud server (e.g., AWS EC2, DigitalOcean, Linode) running Ubuntu 22.04+
  • A non-root user with sudo privileges
  • Python 3.8+ installed
  • A Django project ready for deployment
  • Domain name pointed to your server IP (optional but recommended)
  • Basic understanding of Linux commands

Update the Server & Install Required Packages

update server

sudo apt update && sudo apt upgrade -y

install python 

sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-venv curl git -y
PackageDescription
python3-pipInstalls pip, the package manager for Python. You’ll use this to install Django, Gunicorn, and other Python libraries.
python3-devProvides the header files needed to build Python extensions. Required when installing Python packages that include C extensions (e.g., mysqlclient).
build-essentialA meta-package that includes GCC, make, and other compilers/tools needed to build software from source. Often required when installing Python packages with native extensions.
libssl-devSSL development libraries. Required for secure connections (e.g., when Python packages or apps use HTTPS, cryptography, etc.).
libffi-devForeign Function Interface library. Often used with cryptography and other security-related Python modules.
python3-venvAllows you to create isolated Python environments using python3 -m venv, which is essential for keeping your Django project dependencies clean and separate from system-wide Python.
curlA command-line tool for making HTTP requests. Useful for testing endpoints, downloading scripts, or getting files during setup.
gitVersion control system used to clone your Django project from GitHub or other repositories, and manage code changes.

 

Create a Virtual Environment and Install Requirements

cd /opt/
sudo mkdir myproject && sudo chown $USER:$USER myproject
cd myproject
python3 -m venv venv
source venv/bin/activate
pip install django gunicorn mysqlclient

If you have a requirements.txt file, use:

pip install -r requirements.txt

Why Use /opt/ Instead of /var/www/ for a Django Project?

Traditionally, /var/www/ is used to store static website files served directly by a web server like Apache. However, for modern web applications (like Django with Gunicorn and Nginx), that convention isn't strictly necessary or even ideal.

Reasons Developers Prefer /opt/:

ReasonExplanation
Cleaner separation/opt/ is designed for optional or third-party software — making it a good home for app-specific projects.
Avoids permission issues/var/www/ is typically owned by www-data, which can cause permission headaches during development and deployment.
Less clutterKeeps system files (/var) separate from your custom applications.
Easier to manageYou control ownership with sudo chown $USER:$USER, and tools like Git, pip, or virtualenv work seamlessly.

Create a Non-Root User (if you don't have one)

sudo adduser --disabled-password --gecos "" django

Give User Access to Your Project Directory

sudo chown -R django:www-data /opt/myproject

Configure Django Settings for Production

Edit settings.py:

  • Set DEBUG = False
  • Add your domain to ALLOWED_HOSTS:
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']

Collect static files:

python manage.py collectstatic

Run migrations:

python manage.py migrate

Create superuser (optional):

python manage.py createsuperuser

Set Up MySQL Database

Install MySQL and secure it:

sudo apt install mysql-server -y
sudo mysql_secure_installation

Development libraries for MySQL. Needed to compile and install mysqlclient for connecting Django to a MySQL database.

sudo apt install -y pkg-config libmysqlclient-dev -y

Access the MySQL shell:

sudo mysql

Create a database and user:

CREATE DATABASE myprojectdb CHARACTER SET UTF8;
CREATE USER 'myuser'@'localhost' IDENTIFIED BY 'strongpassword';
GRANT ALL PRIVILEGES ON myprojectdb.* TO 'myuser'@'localhost';
FLUSH PRIVILEGES;
  • myprojectdb: Name of your Django project's database.
  • myuser: MySQL username Django will use to connect.
  • strongpassword: Replace with a secure password.

Tip: Always use strong passwords and unique usernames in production environments.

Update your Django settings.py:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myprojectdb',
        'USER': 'myuser',
        'PASSWORD': 'strongpassword',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

Install MySQL client:

pip install mysqlclient

Run Gunicorn as the Application Server

Test Gunicorn manually first:

gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application

If successful, create a systemd service for Gunicorn:

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

Paste the following:

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
# Replace with your actual Linux username (e.g. ubuntu)
User=django
Group=www-data
WorkingDirectory=/opt/myproject

# Use the correct WSGI module path (project name should match your Django structure)
ExecStart=/opt/myproject/venv/bin/gunicorn --workers 3 --bind unix:/opt/myproject/myproject.sock myproject.wsgi:application

[Install]
WantedBy=multi-user.target

Enable and start Gunicorn:

sudo systemctl daemon-reexec
sudo systemctl enable gunicorn
sudo systemctl start gunicorn

Configure Nginx as a Reverse Proxy

sudo apt install nginx -y
PackageDescription
NginxA high-performance web server used to serve static files and act as a reverse proxy for Gunicorn or other app servers.

Create a new config:

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

Paste:

server {
    listen 80;
    server_name example.com www.example.com;

    # Disable logging for favicon
    location = /favicon.ico { access_log off; log_not_found off; }

    # Serve static files
    location /static/ {
    	# replace with you django static files folder path 
        alias /opt/myproject/static/;
    }

    # Proxy all other requests to Gunicorn via Unix socket
    location / {
        include proxy_params;
        proxy_pass http://unix:/opt/myproject/myproject.sock;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
sudo nginx -t
sudo systemctl restart nginx

Allow traffic on port 80:

sudo ufw allow 'Nginx Full'

Secure with HTTPS (Optional but Recommended)

Use Let’s Encrypt:

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Set up auto-renewal:

sudo certbot renew --dry-run

Final Checklist

  • Gunicorn is running as a service
  • Nginx is correctly serving the Django app
  • Static files are collected
  • MySQL is connected and working
  • HTTPS is enabled (optional)
  • If Gunicorn shows a 500 error use this commad to inspect the logs:   sudo journalctl -u gunicorn -e 
     
Tags:
Ahmad Ali

Ahmad Ali

Hey, I’m Ahmad Ali a tech enthusiast, full-stack web and mobile app developer, and currently working as an Automation Engineer at Nokia. But my curiosity doesn’t stop at the 9-to-5 I’m an all-around tech geek. If it runs on code, connects to a network, or lights up with LEDs... I’m probably into it.

This blog is my digital lab where I mix passion with pixels. Here, you’ll find content on:

  • DIY Electronics & Projects

  • Programming & Automation

  • Data Analysis & Visualizations

  • Telecom & Network Optimization

I’m passionate about solving real-world problems through technology and constantly exploring new tools, languages, and platforms. Whether it’s building smarter systems, automating workflows, or analyzing data for better decisions  I’m always learning, building, and sharing.

Leave a comment

Your email address will not be published. Required fields are marked *

Your experience on this site will be improved by allowing cookies Cookie Policy