Overview

In this guide, I will demonstrate how to install and configure some components on Ubuntu to support and serve Django applications.

We will configure the Gunicorn application server to interface with our applications.

We will then set up Nginx to reverse proxy to Gunicorn, giving us access to its security and performance features to serve our apps.

Prerequisites

  • You have an ubuntu server with sudo privileges
  • You already have you django application running, if not you can check out my Django setup article

Installation

First we will install required packages and libraries

# Python3 Installation

sudo apt-get update
sudo apt-get install python3
sudo apt-get install python3-pip

# Now check python version installed
python -V

Python 3.5.2

# Replace x below with your python version second digit which is 5 in my case
# sudo apt install python3.x-dev
sudo apt install python3.5-dev

sudo apt-get install build-essential
# Install Postgresql
sudo apt-get install libpq-dev  
sudo apt-get install postgresql
sudo apt-get install postgresql-contrib

 

# [OPTIONAL]
# Virtual environment installation

sudo apt-get install virtualenv

# Create a virtual environment
virtualenv -p python3 venv

# Activate virtual environment
source venv/bin/activate
# Nginx Installation

sudo apt update
sudo apt install nginx
# Gunicorn Installation

pip install gunicorn

# Install Postgresql package for django

pip install psycopg2

Create the PostgreSQL Database and User

We’re going to jump right in and create a database and database user for our Django application.

By default, Postgres uses an authentication scheme called “peer authentication” for local connections. Basically, this means that if the user’s operating system username matches a valid Postgres username, that user can login with no further authentication.

During the Postgres installation, an operating system user named postgres was created to correspond to the postgres PostgreSQL administrative user. We need to use this user to perform administrative tasks. We can use sudo and pass in the username with the -u option.

Log into an interactive Postgres session by typing:

sudo -u postgres psql

You will be given a PostgreSQL prompt where we can set up our requirements.

First, create a database for your project:

CREATE DATABASE myproject;

Next, create a database user for our project. Make sure to select a secure password:

CREATE USER myprojectuser WITH PASSWORD 'password';

Afterwards, we’ll modify a few of the connection parameters for the user we just created. This will speed up database operations so that the correct values do not have to be queried and set each time a connection is established.

We are setting the default encoding to UTF-8, which Django expects. We are also setting the default transaction isolation scheme to “read committed”, which blocks reads from uncommitted transactions. Lastly, we are setting the timezone. By default, our Django projects will be set to use UTC. These are all recommendations from the Django project itself:

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, we can give our new user access to administer our new database:

GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

When you are finished, exit out of the PostgreSQL prompt by typing:

\q

Setting Up Django

We need to configure settings.py and main urls.py

Next, find the section that configures database access. It will start with DATABASES. The configuration in the file is for a SQLite database. We already created a PostgreSQL database for our project, so we need to adjust the settings.

Change the settings with your PostgreSQL database information. We tell Django to use the psycopg2 adaptor we installed with pip. We need to give the database name, the database username, the database user’s password, and then specify that the database is located on the local computer. You can leave the PORT setting as an empty string:

# /myproject/myproject/settings.py

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

Configure staticfiles path and url

# settings.py

# You can set your own path or you can do the same

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
STATIC_ROOT = os.path.join(BASE_DIR,"static_root")

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,"media_root")

NOTE: Your project will probably also have static assets that aren’t tied to a particular app. In addition to using a static/ directory inside your apps, you can define a list of directories (STATICFILES_DIRS) in your settings file where Django will also look for static files. 

 
# urls.py

from django.conf import settings
from django.conf.urls.static import static

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

If your debug=False, then run command to collect all staticfiles to your static root:

python manage.py collectstatic

Finally test your project, by running:

python manage.py runserver 0.0.0.0:8000

# [OPTIONAL]
# If you are facing any problem related to port or firewall which might have blocked your port.
# you can look into this article on UFW firewall
# https://raturi.in/blog/how-set-firewall-ufw-ubuntu-debian/

# Make sure after following the above article you have your ssh service running.
# By default when ufw is enabled it block ssh, means you cannot log in to server. 
# Do not close your terminal and start your ssh service by running:

sudo service ssh start or sudo service ssh restart
sudo ufw allow OpenSSH or sudo ufw allow 22

# If you have your ssh port something else then mention your port number
# sudo ufw allow <portnumber>

Testing Gunicorn’s Ability to Serve the Project

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

Note: Change yourproject above to the name of your project folder where wsgi.py is located, basically its in the same folder where settings.py is located.This will start Gunicorn on the same interface that the Django development server was running on. You can go back and test the app again.

We passed Gunicorn a module by specifying the relative directory path to Django’s wsgi.py file, which is the entry point to our application, using Python's module syntax. Inside of this file, a function called application is defined, which is used to communicate with the application.

Creating Gunicorn service file

Open file in: /etc/systemd/system/gunicorn.service and paste the following:

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

[Service] 
User=ubuntu 
Group=www-data 
WorkingDirectory=/path/to/yourproject-directory 
ExecStart=path/to/venv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/path/to/project/project.sock yourproject.wsgi:application 

[Install] 
WantedBy=multi-user.target

NoteReplace WorkinDirectory , ExecStart path to your path

Now save and close the file

To load your service file run:

sudo systemctl daemon-reload

Now start your service by:

sudo service gunicorn start

For now a socket file should have created at your specified WorkinDirectory path: /path/to/project/project.sock if not, verify the process again and then proceed further

To check gunicorn status; you can run:

sudo service gunicorn status

Configure Nginx to Proxy Pass to Gunicorn

Now that Gunicorn is set up, we need to configure Nginx to pass traffic to the process.

Start by creating and opening a new server block in Nginx’s sites-available directory:

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

Inside, open up a new server block. We will start by specifying that this block should listen on the normal port 80 and that it should respond to our server’s domain name or IP address:

Next, we will tell Nginx to ignore any problems with finding a favicon. We will also tell it where to find the static assets that we collected in our ~/myproject/static directory. All of these files have a standard URI prefix of "/static", so we can create a location block to match those requests:

Finally, we’ll create a location / {} block to match all other requests. Inside of this location, we'll include the standard proxy_params file included with the Nginx installation and then we will pass the traffic to the socket that our Gunicorn process created:

server { 
listen 80; 
server_name server_domain_or_IP; 
location = /favicon.ico { 
access_log off; log_not_found off; 
} 
location /static { 
alias /your-static-root-path/; 
}
location /media { 
alias /your-media-root-path/; 
}  
location / { 
include proxy_params; 
proxy_pass http://unix:/your/socket-file-path/yourproject.sock; 
} 
}

NoteChange server_name server_domain_or_IP to your domain or ip. For example: example.com or 192.168.1.0

Note:Change alias /your-static-root-path/ to your static_root path. For example alias /home/nitin/project/static_root/

Note:Change alias /your-media-root-path/ to your media_root path. For example alias /home/nitin/project/media_root/

Notechange proxy_pass http://unix:/your/socket-file-path/yourproject.sockto your .sockfile 

Save and close the file when you are finished.

Now, we can enable the file by linking it to the sites-enabled directory:

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

Test your Nginx configuration for syntax errors by typing:

sudo nginx -t

If no errors are reported, go ahead and restart Nginx by typing:

sudo service nginx restart

You should now be able to go to your server’s domain or IP address to view your application.