Deploy Django on Ubuntu Using PostgreSQL, Gunicorn, Nginx

How to deploy a Django project on an Ubuntu server using PostgreSQL, Gunicorn, Nginx
July 25, 2024 by
Deploy Django on Ubuntu Using PostgreSQL, Gunicorn, Nginx
Hamed Mohammadi
| No comments yet

In this post we go through the process of deploying a Django project called “mysite” to production environment on an Ubuntu server. For database PostgreSQL is used and the Django application is run behind Nginx proxy using gunicorn.

1. The Required Packages

We use apt package manager to install the required package on Ubuntu:

$ sudo apt update
$ sudo apt install python3-venv python3-dev libpq-dev postgresql postgresql-contrib nginx curl

This is packages needed by Django and Gunicorn. PostgreSQL database manager is also installed and we use it as our database for our Django project.


2. PostgreSQL Database and User

We need a PostgreSQL database and user for our Django project. First we log in as psql:

$ sudo -u postgres psql

Now we can create our needed database:

# CREATE DATABASE mysite;

Then we should create a user:

# CREATE USER mysiteuser WITH PASSWORD 'STRONGPASSWORD';


Now we set some connection parameters that is preferred for Django:

# ALTER ROLE mysiteuser SET client_encoding TO 'utf8';
# ALTER ROLE mysiteser SET default_transaction_isolation TO 'read committed';
# ALTER ROLE mysiteuser SET timezone TO 'UTC';

Finally change the owner on the newly created database to our database user:

# ALTER DATABASE mysite OWNER TO mysiteuser;

Our database is ready for Django connection. Press CNTR+D to exit psql.



3. Clone Your Django Project from GitHub and Prepare It

We assume your project lives in a GitGub repository. First make sure that git is installed on your server:

$ sudo apt update 
$ sudo apt install git 

Create a system user for your Django project. We will use this system user for running our Django project using Gunicorn:

$ sudo useradd -m -d /opt/mysite -U -r -s /bin/bash mysite

 Log in to this new user:

$ sudo su – mysite

Make sure you are at /opt/mysite and create static and media directories for storing our Django static and media files:

$ cd /opt/mysite
$ mkdir static media

The user www-data should have access to these two folders, because nginx could not access static and media file if www-data user cant access these folders. Run this command using your sudoer user later:

$ sudo usermod -a -G mysite www-data
 

Now clone your Django project to home directory of your system user at /opt/mysite:

$ git clone git@github.com:you/mysite.git

 Note that currently you need to set up ssh login for GitHub to clone your Django repository if it is not public. You can use ssh-keygen first to create a public key and copy the generated key to your GitHub settings.

Go to the cloned repository folder and create a virtual environment and enable it:

$ cd /opt/mysite/mysite
$ python3 -m venv .venv
$ source .venv/bin/activate

 Hopefully you have a requirements.txt file at the root of your Django project that we can use it to install required package using pip:

(.venv) $ pip install -r requirements.txt

Now install Gunicorn and PostgreSQL database driver:

(.vent) $ pip install gunicorn psycopg2-binary

We need to change some settings in our Django settings.py file:

DEBUG = False
ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', . . ., 'localhost']

 Next we set our database connection parameters in settings.py:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'mysite',
        'USER': 'mysiteuser',
        'PASSWORD': 'STRONGPASSWORD',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

 Finally set static and media settings:

STATIC_URL = 'static/'
STATIC_ROOT = '/opt/mysite/static'

MEDIA_URL = 'media/'
MEDIA_ROOT = '/opt/mysite/media'

 This is the required settings we should set. Save the file.

Do all migrations and also create a superuser:

(.venv) $ python manage.py makemigrations
(.venv) $ python manage.py migrate
(.venv) $ python manage.py createsuperuser


Now you can run collectstatic command:

(.venv) $ python manage.py collectstatic


To test your Django project first make sure that if you use ufw firewall prot 8000 connection is allowed (run by your sudoer user):

$ sudo ufw allow 8000

 Now test your Django project by running the development server:

(.venv) $ python manage.py runserver 0.0.0.0:8000


Test your Django site is running by going to http://server_domain_or_IP:8000 in your browser.

The last thing we check is running our Django project using Gunicorn:
(.venv) $ gunicorn --bind 0.0.0.0:8000 mysite.wsgi


If you can see your project home page everything is right till now and you can exit from mysite system user using CNTR+D.



4. Create System Socket and Service

We should create a Gunicorn socket and service to listen and direct connection to our Django site. First create the socket:

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

 In that file type:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target


Next we should create systemd service file:

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

 In that file type:

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

[Service]
User=mysite
Group=www-data
WorkingDirectory=/opt/mysite/mysite
ExecStart=/opt/mysite/mysite/.venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          mysite.wsgi:application

[Install]
WantedBy=multi-user.target

 Now Start the Gunicorn socket:

$ sudo systemctl start gunicorn.socket
$ sudo systemctl enable gunicorn.socket

Check the status of the process to find out whether it was able to start:

$ sudo systemctl status gunicorn.socket

 You should receive an output like this:

● gunicorn.socket - gunicorn socket
     Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2024-07-21 16:11:37 UTC; 3 days ago
   Triggers: ● gunicorn.service
     Listen: /run/gunicorn.sock (Stream)
     CGroup: /system.slice/gunicorn.socket

Jul 21 16:11:37 302477 systemd[1]: Listening on gunicorn socket.

 Finally change static and media folders access rights, so www-data can access these folder, because this will not allow it to get access to static and media folders:

$ sudo usermod -a -G mysite www-data
$ sudo chmod -R g+rw /opt/mysite/static
$ sudo chmod -R g+rw /opt/mysite/media

5. Configure Nginx to Proxy Connections to Gunicorn

In this section we set Nginx site that passes the proxy to Gunicorn. For SSL we get certificate using Let’s Encrypt. Follow steps from Let’s Encrypt Certificate on Ubuntu and Nginx to get ssl certificate for your site.

When you are finished with steps in above guide change the nginx site configuration to:

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

    include snippets/letsencrypt.conf;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name www.mysite.com;

    ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/mysite.com/chain.pem;
    include snippets/ssl.conf;
    include snippets/letsencrypt.conf;

    return 301 https://mysite.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name mysite.com;

    ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/mysite.com/chain.pem;
    include snippets/ssl.conf;
    include snippets/letsencrypt.conf;

    # . . . other code

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /opt/mysite/;
    }

    location /media/ {
            root /opt/mysite;
    }

    location / {
        include proxy_params;

        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

 

Now check if Nginx site configuration is Okay:

$ sudo nginx -t

And then restart the Nginx service:

$ sudo systemctl restart nginx


Finally remove 8000 port from allowed in ufw and enable Nginx:

$ sudo ufw delete allow 8000
$ sudo ufw allow 'Nginx Full'

Your Django project setup to production is complete. Feel free to ask any questions about any errors you may encounter during this process in the comments.


Deploy Django on Ubuntu Using PostgreSQL, Gunicorn, Nginx
Hamed Mohammadi July 25, 2024
Share this post
Tags
Archive

Please visit our blog at:

https://zehabsd.com/blog

A platform for Flash Stories:

https://readflashy.com

A platform for Persian Literature Lovers:

https://sarayesokhan.com

Sign in to leave a comment