Sign In
Sign In

How to Install Django, Nginx, and Gunicorn on a Virtual Server

How to Install Django, Nginx, and Gunicorn on a Virtual Server
Hostman Team
Technical writer
Django
20.09.2024
Reading time: 8 min

Django is a web framework for developing mobile applications and websites using Python. It includes a built-in server for local testing and simple solutions, but you'll typically need a more robust setup for production. This guide will walk you through installing Django on Ubuntu and configuring it for production.

Prerequisites

  • A VPS running Ubuntu
  • Basic firewall settings
  • A user with sudo privileges

Do not perform this setup as the root user; use a user with sudo privileges instead.

Install Packages from the Ubuntu Repository

First, let's update the apt packages to ensure we use the latest releases. Then, install Python 3 and other necessary packages:

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

This command will install pip (Python's package manager), Python-related files, Gunicorn (a Python WSGI HTTP server), PostgreSQL (a relational database system), and Nginx (a web server).

Create a Database and User in PostgreSQL

PostgreSQL uses "peer authentication" by default, allowing users to connect without entering a password if the OS user matches the database user. You can switch to the postgres system user using the following command:

sudo -u postgres psql

In the PostgreSQL interface, create a new database for your project:

CREATE DATABASE myproject;

Then, create a user with a secure password:

CREATE USER myprojectuser WITH PASSWORD 'password';

To optimize settings, configure the user with UTF-8 encoding, "read committed" transaction isolation, and the UTC timezone:

ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';

Now, grant all privileges on the database to this user:

GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

Exit the PostgreSQL prompt:

\q

Your PostgreSQL database setup is now complete.

Set Up a Python Virtual Environment

You'll need to install virtualenv to create an isolated environment for your project:

sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv

Create a new directory for your project and navigate to it:

mkdir ~/myprojectdir
cd ~/myprojectdir

Now, create a virtual Python environment:

virtualenv myprojectenv

Activate the virtual environment:

source myprojectenv/bin/activate

You’ll see the prompt change, indicating that you're now working inside the virtual environment:

(myprojectenv)user@host:~/myprojectdir$

Install Django, Gunicorn, and the PostgreSQL adapter psycopg2:

pip install django gunicorn psycopg2-binary

Create and Configure Your Django Project

Now, create a new Django project inside the directory:

django-admin startproject myproject ~/myprojectdir

Modify Django settings. Open the settings file to configure it:

nano ~/myprojectdir/myproject/settings.py

At the top of the file, add the following:

import os

Next, find the ALLOWED_HOSTS directive. This determines which addresses can connect to your Django instance. Add the necessary IP addresses or domains within square brackets:

ALLOWED_HOSTS = ['your_server_domain_or_IP', 'second_domain_or_IP', 'localhost']

Then, scroll to the DATABASES section. Replace the default configuration with the PostgreSQL settings:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

At the end of the file, define where Django should store static files. This will be needed when setting up Nginx:

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

Save and exit the file.

Now, apply database migrations and create a superuser to manage the project:

~/myprojectdir/manage.py makemigrations
~/myprojectdir/manage.py migrate
~/myprojectdir/manage.py createsuperuser

Set a username, password, and email. After this, if you plan to collect all static content in one place, run the following command:

~/myprojectdir/manage.py collectstatic

This will require confirmation before moving files to the static folder. Allow access to port 8000 through the firewall using UFW:

sudo ufw allow 8000

Start the Django development server:

~/myprojectdir/manage.py runserver 0.0.0.0:8000

Visit http://server_domain_or_IP:8000 in your browser to verify that everything is working. If the Django index page appears, your setup is successful! You can also access the admin panel at http://server_domain_or_IP:8000/admin using the superuser credentials you created.

To stop the development server, press <Ctrl+C> in the terminal.

Creating Socket and systemd Files for Gunicorn

Gunicorn creates a socket at startup via systemd, which listens for incoming connections. First, create a new file:

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

In this file, create a new [Unit] section to describe the socket, a [Socket] section to define where the socket is located, and an [Install] section for enabling the socket at startup:

[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target

Save and close the file. Next, create the systemd service file:

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

Start with the [Unit] section to define metadata and dependencies, specifying that the service should start after the network is up:

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

In the [Service] section, define the user, group, working directory, and command to start Gunicorn. Replace user with your username. In this example, we use 3 worker processes:

[Service]
User=user
Group=www-data
WorkingDirectory=/home/user/myprojectdir
ExecStart=/home/user/myprojectdir/myprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          myproject.wsgi:application

Finally, in the [Install] section, define where the services should be linked upon activation:

[Install]
WantedBy=multi-user.target

Close the file and save the changes.

Now, start the Gunicorn socket and enable it to run at boot:

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

Checking the Gunicorn Socket

To verify that the Gunicorn socket is running, check its status:

sudo systemctl status gunicorn.socket

Make sure the gunicorn.sock file exists in the /run directory:

file /run/gunicorn.sock

The output should confirm that the socket exists:

/run/gunicorn.sock: socket

If the socket fails to start, use the following command to diagnose the issue:

sudo journalctl -u gunicorn.socket

Check for any errors in the file, and correct them if necessary.

Diagnosing Socket Activation

It's important to note that when gunicorn.socket is running, gunicorn.service will be inactive. You can confirm this with:

sudo systemctl status gunicorn

The output should show the service is inactive. 

Now, test the socket connection with curl:

curl --unix-socket /run/gunicorn.sock localhost

If you see HTML data, Gunicorn has successfully started. Recheck the status of the service:

sudo systemctl status gunicorn

The status should be Running. If you encounter errors, check the Gunicorn log for details:

sudo journalctl -u gunicorn

Reload and restart the systemd daemon if needed:

sudo systemctl daemon-reload
sudo systemctl restart gunicorn

Configuring Nginx as a Reverse Proxy for Gunicorn

Now, set up Nginx as a reverse proxy. Create a new Nginx configuration file:

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

In this file, configure Nginx to listen on port 80 and respond to your domain or server IP address:

server {
    listen 80;
    server_name server_domain_or_IP;
}

Next, define the location of static files and instruct Nginx to ignore issues related to favicon.ico:

server {
    listen 80;
    server_name server_domain_or_IP;
    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/myprojectdir;
    }
}

Finally, create a block that passes all other requests to the Gunicorn socket:

server {
    listen 80;
    server_name server_domain_or_IP;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/myprojectdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Save and close the file. Link this configuration to the sites-enabled directory:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Add the www-data user to your group:

sudo usermod -a -G ${USER} www-data

Check for syntax errors in the Nginx configuration:

sudo nginx -t

If no errors are found, restart Nginx:

sudo systemctl restart nginx

Now, delete the rule allowing access through port 8000, and open port 80 for Nginx:

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

Finally, visit your server's domain or IP address to confirm everything works.

Before going live, make sure to secure your site with SSL/TLS using a certificate from Let’s Encrypt.

Conclusion

This guide has walked you through installing and configuring Django with PostgreSQL, Gunicorn, and Nginx on Ubuntu. Once configured, you can start building your Django application in this environment.

Django
20.09.2024
Reading time: 8 min

Do you have questions,
comments, or concerns?

Our professionals are available to assist you at any moment,
whether you need help or are just unsure of where to start
Email us