Connect with us


How To setup Django with Postgres, Nginx, and Gunicorn on Ubuntu 20.04

Django is a free and opensource high-level Python web framework that you can use to get your Python web applications up and running. Maintained by Django Software Foundation, Django ships with a development server that you can use for giving your code a test run on your local system. However, if you are aiming at deploying an application on a production server, you need a more powerful and secure web server. Gunicorn is a fast and lightweight WSGI HTTP server that is compatible with most web frameworks such as Django. It takes away a great deal of workload that a web application needs to undertake by performing the following:

  • Handling HTTPS connections.
  • Hosting static files.
  • Ensuring that an application can scale with ease.
  • Recovering from application crashes.

In this guide, we are going to install Gunicorn with Postgres, Nginx and Gunicorn on Ubuntu 20.04. We are going to set up a PostgreSQL database engine in the place of the default SQLite database. Thereafter, we will install and set up the Gunicorn application server that will interface with our application. The Nginx web server will be used to reverse proxy to Gunicorn, allowing us to access its much-needed performance and security features to serve our apps.


To get started, ensure that you have a fresh instance of Ubuntu 20.04 LTS with a regular user with administrative privileges. Also, ensure that you have a stable internet connection for installing the various components that are required.

Step 1: Install the requisite Packages

To start off, we will begin with the installation of the requisite software packages from Ubuntu repositories. Later, we will proceed to use pip – Python’s package manager – to install additional requisite packages.

First, begin by updating the system package list.

Next, we are going to install pip3, the Python development files that will prove essential when building Gunicorn, PostgreSQL database, Nginx web server and all the libraries and dependencies required to interact with PostgreSQL. Therefore, execute the command:

Once the installation is complete, we are going to create the PostgreSQL database and user and later on optimize the database

Step 2: Creating a database and database user in PostgreSQL

The next step is to create a database and database user for the Django application.

For local connections, Postgres uses a mode of authentication called “peer authentication” This is where if a valid Postgres username matches a system’s username, then the user logs in without the need for any authentication.

Upon installation, PostgreSQL creates a user called ‘postgres‘ which corresponds to PostgreSQL admin user. We will use this user to log into the database as shown.

Once you’ve logged in, create the project’s database as shown:

Next, create the database user

Once the user is created, we are going to make a few tweaks to the connection parameters to optimize database operations. Django project recommends the following

First, we will configure the default encoding to UTF-8 as per Django’s requirements.

Next, we will set the default transaction isolation scheme to “read committed”.

And lastly. we will set the timezone to UTC.

Now grant the user access to manage the database as shown:

To exit from PostgreSQL prompt, run:

create database for Gunicorn project

Perfect! We have in place the database for our Django project. Next, we are going to prepare a Python Virtual Environment for our project.

Step 3: Creating a Python virtual environment for Django Project

With our database in place, let get along with setting up other project requisites. We shall set up a virtual environment with which we shall use to install additional Python requirements.

First, we shall install and upgrade pip3 by running the following command:

Next, using pip3 install the virtual environment as shown:

Great, with the virtualenv in place, we can begin creating our project. We will create a project directory and navigate into it using the commands below.

Inside the project directory, create a virtual environment as shown.

Create virtual environment for Gunicorn project

This creates a new directory called ‘my_projectenv‘ inside the ‘my_project‘ directory. Within the ‘myprojectenv‘ directory, we are going to install a local Python and pip version.

But before we do so, we will activate the virtual environment by executing the command:

The prompt will now change to :

Within the virtual environment, you will install Gunicorn, Django and psycopg2 which is PostgreSQL’s adaptor using pip. Therefore, execute the command:

install Gunicorn Django and psycopg2

You now have all the prerequisite software packages for the Django project.

Step 4: Creating and Setting up a new Django project

We already the Python requirements already in place. Let’s now touch base and begin creating actual project files for Django.

Creating the Django project

We are going to instruct Django to create a second-level directory that holds the actual code and also accommodates a management script. Therefore run the command:

Inside the ~/my_project directory, you should now have the following files:

  1. django_project: This is Django’s project package. It should contain the,,, and files.
  2. This is Django’s management script.
  3. my_projectenv: This is the virtual environment directory created earlier.

Modifying the project settings

We need to make a few tweaks to the settings of the project files. To do so, open the ‘‘ file using the text editor of your choice.

Scroll and locate the ‘ALLOWED_HOSTS‘ directive. This specifies a list of IP addresses of domain names that can connect to your Django instance. Inside the square brackets, list the IP addresses that are associated with your Django server. Ensure that you enclose each entry with single quotation marks and separated by a comma as shown below.


Ensure that you include the ‘localhost‘ option since connections will be queried using the locally installed Nginx instance.

Next, scroll to the ‘DATABASES‘ section. The default settings are for SQLite database and we need to change that since our Django project will use the PostgreSQL database.

Comment the ‘ENGINE‘ attribute since we are not using the SQLite database and the ‘NAME‘ attribute as well since we are going to specify our own database name that we created in PostgreSQL.

For the ‘ENGINE‘ attribute, instruct Django to use psycopg2 adapter and specify the database name, username, password and host as shown.

Thereafter, scroll all the way to the bottom and append the following line:

This line indicates where static files should be placed and this is to simplify the handling of requests by Nginx web server.

Finally, save and exit from the configuration file.

Step 5: Finalize the initial project setup

The first step in completing the initial project set up is to migrate the initial database schema to PostgreSQL database via the ‘‘ management script.

Run the commands below:

Next, create an administrative user for the project as shown:

You will be required to provide an email address, provide a password and then confirm it.

Then gather all the static content into the directory

These static files will be stored inside a directory called ‘static‘ located within your directory.

Configuring the firewall

We are going to run the test Django project via port 8000, and with this in mind, we are going to allow the port on the firewall. Therefore run the commands below:

Then finally, you can test the Django development server by running the command:

The Django development server has now been started and is listening on port 8000. To view its front end, open your browser and browse your server’s IP address as shown:

If all went according to plan, you should get the default Django development page shown.

To log in as the admin user, append the ‘/admin' suffix at the end of the URL. Enter the username and password you provided when creating the administrative user using the createsuperuser attribute.

Click on the ‘Login‘ button and this will usher you to the admin dashboard.

When you are done, hit CTRL + C to exit and stop the development server.

Step 6: Ensuring that Gunicorn can serve the Django Project

We have successfully tested our Django development server and ensured that it’s running as expected on the browser. Now we need to test the ability of Gunicorn’s to serve the Django project.

To do so, we will navigate into the Project’s folder and load the WSGI module

This will launch Gunicorn’s interface. However, the interface will not have the CSS styling since Gunicorn does not have any idea where to find the static CSS content.

Once again, when you are done testing hit the CTRL + C to quit and stop Gunicorn. Having finished testing Django’s project, run the command below to exit from the virtual environment.

The virtual environment prompt will now disappear.

Step 7: Making Gunicorn a systemd service and create service files

Having confirmed that Gunicorn can interact with Django’s app server, we are going to implement a more robust and efficient wat to start and stop the Django application server. For this, we need to create a systemd service and socket files.

Create a Gunicorn socket file

Gunicorn socket is going to be created on boot up and will begin to listen for incoming connections. When a connection happens, systemd will automatically trigger the start of Gunicorn to handle the connections.

We are going to create Gunicorn’s systemd socket file as shown:

We are going to add 3 sections. First, we will define the [unit] section to define the socket, [Socket] section to specify the Socket’s location and finally, the [Install] section to ensure the timely creation of the Socket.

Save the configuration file and exit.

Create Gunicorn systemd file

Next, we are going to create a systemd file for Gunicorn as shown:

We will start with the the [unit] where we will define the dependencies and metadata. We will define a short description of the service and instruct init system to start only if the networking target is reached. Since the service relies on the socket, we will specify a 'Requires' directive.

Next, we are going to define the [Service] option where we shall specify a user and group that the process will run under. Additionally, we shall grant the regular user ownership of the process and grant group ownership to www-data group for Nginx to communicate seamlessly with Gunicorn.

We are then going to map out a working directory and define a command for starting the service. In particular, we will define the full path to Gunicorn’s executable installed in the virtual environment. Later we shall bind the process to the Unix socket located in the /run directory for the process to smoothly communicate with Nginx.

Lastly, we shall append an [Install] section to specify the run-level to use upon booting, which in this case is multi-user.

The complete systemd file is as shown:

Configure Gunicorn systemd file

Save and exit the configuration file.

Step 8: Managing Gunicorn socket

Now start and enable the Gunicorn socket.

Start Gunicorn socket

To verify that Gunicorn systemd service is running, execute the command:

Check status of Gunicorn socket

Additionally, verify the existence of the gunicorn.sock file in the /run directory.

Check the existence of Gunicorn socket file

If you encounter an error, check out the configuration file /etc/systemd/system/gunicorn.socket to fix any errors before proceeding.

Step 9: Testing Socket Activation

To test the socket activation, we need to use the curl command to send a connection to the socket.

The command will print hundred of HTML code clearly indicating that Gunicorn service was started and now has the ability to serve your Django application.

To verify that Gunicorn is running smoothly, execute the command:

If you get an error from checking the status of Gunicorn service or from running the curl command, check /etc/systemd/system/gunicorn.service configuration file for errors.

Once you’ve made the changes to the configuration file, reload the daemon and restart Gunicorn’s service.

Step 10: Setup Nginx to proxy pass to Gunicorn

This is the very last step. Here, we will configure Nginx to pass traffic to Gunicorn and serve Django’s application content. we will begin by creating a new server block file in the sites-available shown.

Begin by specifying that the block will listen to HTTP port 80 and at the same time respond to the server’s IP or domain name.

Thereafter, we will add a few lines instructing Nginx to overlook any issues in locating a favicon. Additionally, we shall tell Nginx where to locate the static assets collected in our ~/my_project/static directory.

Lastly, we shall create a location / {} block that will match all other requests. Inside the block, we shall include a standard proxy_params file that comes included with Nginx installation and then pass traffic straight to the Gunicorn socket.

The final configuration file looks as shown:

setup Nginx to proxy pass  to Gunicorn

Save and exit the configuration file. Now enable the file by creating a soft link to the /etc/sites/enabled directory.

To ensure that all the Nginx settings are error-free execute the command:

Perfect, now restart Nginx web server for all the changes to take effect.

On the firewall, allow Nginx traffic and delete the firewall rule that opens port 8000 since Nginx will be now be handling the Django content via Gunicorn.

From now going forward, the Django application content will be handled by Gunicorn and can be accessed by simply typing the server’s IP address.

Continue Reading