Deploy Django with Postgres, Nginx, and Gunicorn on Ubuntu 20.04 VPS
Introduction
What is Django? Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development, so we can focus on writing your app without needing to reinvent the wheel.
It’s free and open source.
If we develop our application with the help of Django, there is no need to run any 3rd party applications to run our project locally, Django comes with an embedded development server with nearly all features any developer needs during the development stage. Though when we try to deploy our application things are changing because the Django development server is not the proper choice for production because of security, performance.
Prerequisites
We have to consider some tech stacks we are going to use during the Django deployment process.
- First of all, we are going to use Ubuntu 20.04 as our OS in VPS we'll as a web server. The best practice is using fresh OS and a user without
sudo
privileges. - Gunicorn is a Python WSGI HTTP Server for UNIX that will be running behind
nginx
- the front-facing web server. NGINX takes a coming HTTP request and sends it togunicorn
server. - NGINX is open-source software for web serving, reverse proxying, caching, load balancing, media streaming, and more. For now, we will only use it as a Web Server.
- Supervisor is a client/server system that allows its users to monitor and control several processes on UNIX-like operating systems. We will use
supervisor
to control our processes (gunicorn
) without dealing withrc.d
scripts. - If we want to serve our server over HTTPS, we just need to use Certbot to provide a certificate for our server
Time to deploy
Launch VPS instance
We need any type of VPS (AWS, GCP, Hetzner, etc.) with SSH, HTTP, HTTPS ports open to deploy our application on it. I won't go further on how to create VPS instances on some of these cloud services, because it's out of topic. But to put it briefly:
- I will start a new VPS instance with
Ubuntu 20.04
. - Allow
SSH (22), HTTP (80), HTTPS (443)
ports. We'll be using ssh to connect our instance remotely and HTTP, HTTPS to serve web applications. - I suggest using an SSH key to make our application more secure. To archive, in the instance launching step generate new key pair and simply download it.
- Connect to remote VPS instance with the help of an SSH key. To guaranty permissions of our ssh key for making a connection:
It's time to connect our VPS:
vps_user - is a user that is created automatically or manually during VPS launch based on which cloud service you use.
vps_ip - is the public IP address of your VPS instance.
Let's update and upgrade OS packages before starting our deployment:
Deploy Django application with gunicorn
and supervisor
Firstly, we need to check if python3 is installed, else install it:
Next, to have isolated space for our python
projects we have to set up a virtual environment. With the help of venv
, we'll be sure our programs have their of the set of dependencies
Now, let's clone our Django
project from the remote git repository and install dependencies. Note: You can use the same example Django application for deployment
It's time to set up gunicorn
and supervisor
to run our Django app and manage it
After successful installation, let's create a log file directory for gunicorn
program we are going to write:
Later, we need to write our gunicorn
program:
Here is an example gunicorn
program for this Django application:
/home/ubuntu/django-example
- the path of the Django application we are going to deploy. core.wsgi:application
- core
is base project folder settings.py and wsgi.py
locates.
As the last step, we only have to start our supervisor
service:
Set up and connect our app to Postgresql database
For now, our application working correctly, but if you noticed we haven't run any migrations. Because we don't have any connected database for our application. Django uses the sqlite3
database for development purposes, but it's not recommended database for production. We'll be configuring and use `PostgreSQL for our Django application.
Let's install all packages for Postgresql
:
Postgresql
is now successfully installed, we just need to create our database and database user:
Our database is ready to use, but if we didn't, we have to configure our Django application to use the database information we momentarily created. Default Django database engine uses sqlite3, but we want to change it to PostgreSQL engine. We can do this simply using the
psycopg2` package:
The database is ready to go. Let's fire up the migrations:
Set up NGINX
web server
Okay, our application now working perfectly, but cannot access our application directly for now. In this step, NGINX
comes to lend a helping hand to map our gunicorn
program into web server requests. Let's start to install and configure the NGiNX
web server:
If we want nginx
to listen to our working gunicorn
application, we have to do some simple configurations:
- Let's create a config file for our application to
nginx
:
- With the simple
nginx
config we can listen to ourgunicorn
application:
Change 192.168.1.1
to your VPS public IP address and /home/ubuntu/django-example/
to the directory of your application.
We know that in production Django doesn't serve static files, so we need to configure nginx
to serve static files too. Change /home/ubuntu/django-example/static
to your STATIC_ROOT
directory.
If I remember correctly, we didn't collect our static files for the production environment.
Next, we need to test our nginx
configuration. If everything is in order, we are going to only create a symbolic link to connect our configuration file to the sites-enabled
directory.
Finally, restart the nginx
service and check VPS public IP address:
Connect custom domain to Django application
Okay, that's good to hear our application is now accessible all over the web. But won't be easy to access our application using a custom domain like teletubbies.com
?. I think, yes.
Then let's configure a custom domain for our application. To achieve this, we need domain and DNS (like Route 53, Cloud DNS, etc). I won't cover specific DNS services of any cloud service like Route 53 because I don't want to get off the topic. To make it short:
- Create DNS zone for application
- Add your custom domain to the DNS zone.
- Provide domain name servers with DNS name servers. For example:
- Create an
A
record to connect your VPS public IP address to the DNS. - Add a custom domain to
ALLOWED_HOSTS
in yoursettings.py
file - You can now change
nginx
to listen to a custom domain instead of IP. But don't forget to test and restartngixn
after changing configurations.
Now time to check if your custom domain is working.
Serve Django application with TSL (SSL)
Providing HTTPS connection for applications is a greater advantage because in this way we are protecting our connection with a security certificate.
As the last step, let's add a TSL certificate for our application:
After adding repositories, we have to install and set up certbot:
Later, to secure our server, just restart the server:
At last, we have deployed our Django application with the help of Gunicorn
, supervisor
, Nginx, and
PostgreSQL` on Ubuntu 20.04