Let's start at the beginning: what is Django?
Django is a web framework that can help your Python application or website take off. Django includes a simplified development server for testing code locally, but any production service requires a more secure and powerful web server.
In this post, we'll explain how to install and configure some components on Ubuntu 16.04 to support and serve Django applications. First, we'll create a PostgreSQL database and configure the Gunicorn application server to interact with our applications. Then, we'll configure Nginx as a reverse proxy for Gunicorn, giving us access to its security and performance features to serve our applications.
Yes, we know it's a bit of work, but you'll see that once it's done, you'll be very satisfied with the result 🙂
The first requirement for this post to be useful is that your VPS Server must be a clean installation, meaning it must be freshly installed with only Ubuntu 16.04, and have a non-root user with “sudo” privileges.
Install packages from the Ubuntu repositories.
To begin the process, you'll need to download and install everything you need from the Ubuntu repositories. We'll also use Python's pip package manager later to install additional components.
To do this, you'll need to update the local apt package index and then download and install the packages. The packages you install will depend on the version of Python your project will use.
If you're using Python 2, run these commands:
# sudo apt-get update
# sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib nginxIf, instead, you're using Django with Python 3, enter:
# sudo apt-get update
# sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginxThis will install pip, the Python development files needed to install Gunicorn later, the Postgres database system and the libraries needed to interact with it, and the Nginx web server.
Create the PostgreSQL database and users.
Now we'll create the database and the database user for our Django application.
By default, PostgreSQL uses an authentication scheme called “peer authentication” for local connections. Basically, this means that if the operating system username matches a valid Postgres username, that user can log in without additional authentication.
During the Postgres installation, an operating system user named postgres is created to correspond to the PostgreSQL administrative user. You must use this user to perform administrative tasks. You can use “sudo” and enter the username with the “-u” option.
Log in to an interactive Postgres session by typing:
# sudo -u postgres psqlA PostgreSQL prompt will now appear, where you can configure your requirements.
First, create a database for your project:
CREATE DATABASE miproyecto;
CREATE USER usuariomiproyecto WITH PASSWORD ‘contraseña’;
Next, you need to modify some connection parameters for the user you just created. This will speed up database operations so that the correct values don't have to be looked up and configured every time a connection is established.
You must set the default encoding to UTF-8, which is what Django expects. You also need to set the transaction isolation level to “read committed”, which blocks reads of uncommitted transactions. Finally, you'll need to set the time zone.
By default, Django projects will be set to use UTC. These are all recommendations from the Django project itself:
ALTER ROLE usuariomiproyecto SET client_encoding TO ‘utf8’;
ALTER ROLE usuariomiproyecto SET default_transaction_isolation TO ‘read committed’;
ALTER ROLE usuariomiproyecto SET timezone TO ‘UTC’;Now you can give your new user access to administer your new database:
GRANT ALL PRIVILEGES ON DATABASE miproyecto TO usuariomiproyecto;When you're finished, exit the PostgreSQL prompt by typing “\q”
Create a Python Virtual Environment for your project
Now that you have your database, you can work on the remaining requirements to get the project ready. We'll install our Python requirements inside a virtual environment to make management easier.
To do this, the first thing you need is access to the virtualenv command. You can install it with pip.
If you're using Python 2, use the command:
# sudo pip install virtualenvIf you're using Python 3, it will be:
# sudo pip3 install virtualenvWith virtualenv installed, you can start setting up your project. Create the directory to store your project files and enter it:
# mkdir ~/miproyecto
# cd ~/miproyectoInside the project directory, create a Python virtual environment with the command:
# virtualenv miproyectoentornoThis will create a directory named “miproyectoentorno” inside the “miproyecto” directory. Inside it, it will install a local version of Python and a local version of pip. You can use this to install and configure an isolated environment for the project.
Before installing the Python requirements for your project, we need to activate the virtual environment. You can do this by typing:
# source miproyectoentorno/bin/activateThe prompt should change to indicate that you're now operating inside a Python virtual environment. It should look something like this:
(miproyectoentorno)user@host:~/miproyecto$.With the virtual environment active, we'll install Django, Gunicorn, and the psycopg2 PostgreSQL adapter using the local instance of pip:
# pip install django gunicorn psycopg2Regardless of the Python version you're using, when the virtual environment is activated, you must use the pip command (not PIP3)
Create and configure a new Django project
With the Python components installed, you can create the files for our Django project.
Create the Django project
Since you already have a project directory, you need to tell Django to install the files in it. This will create another directory inside the main one with the actual code, which is normal, and place a management script in this directory.
# django-admin.py startproject miproyectoAdjust the project settings
The first thing you should do with your newly created project files is adjust the settings. Open the configuration file with a text editor:
# nano miproyecto/settings.pyFirst, find the section that configures database access. It will begin with DATABASES. The configuration in the file is for a SQLite database. We've already created a PostgreSQL database for our project, so we need to adjust the configuration.
Change the configuration to use the information for the PostgreSQL database. Then you need to tell Django to use the psycopg2adaptor adapter you installed with pip. You need to provide the database name, user, and password, and then indicate that the database is local. You can leave PORT as an empty string, for example ~/miproyecto/miproyecto/settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'miproyecto',
'USER': 'usuariomiproyecto',
'PASSWORD': 'contraseña',
'HOST': 'localhost',
'PORT': '',
}
} Next, go to the end of the file and add the value specifying where static files should be stored. This is necessary so NGINX can handle requests for these objects. The following line tells Django to put them in a directory named “static” in the project's base directory:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')Save and close the file when you've finished making changes.
Complete the initial project setup
Now we can migrate the initial database schema to our PostgreSQL database using the management script:
# cd ~/miproyecto
# ./manage.py makemigrations
# ./manage.py migrateCreate an administrative user for the project by typing:
# ./manage.py createsuperuserYou'll need to select a username, provide an email address, and select and confirm a password.
You can collect all static content in the directory location you configured by typing:
# ./manage.py collectstaticYou'll need to confirm this operation. Static files will be saved in a directory named “static” inside the project directory.
To run tests now, port 8000 must be open on your server. Check whether the port is already open for your server, and if not, open it by following this other article in our knowledge base:
Manage rules for a firewall profile in the client panel
Once the port is open, you can test the project by starting the Django development server with the command:
# ./manage.py runserver 0.0.0.0:8000Now, to check it, use any browser to access your domain or your server's IP, followed by port 8000: http://IP.DE.TU.SERVIDOR:8000
You should see the default Django page:
If you add /admin to the end of the URL in the address bar, you can log in with the administrative username and password you created with the “createsuperuser” command
After authenticating, you'll be able to access Django's default administration interface:
When you've finished exploring the interface, press “CTRL+C” in your server's console window to stop the development server.
Test Gunicorn's ability to serve the project
The last thing we want to do before leaving the virtual environment is test Gunicorn to make sure it can serve the application. We can do this easily by typing:
# cd ~/miproyecto
# gunicorn -bind 0.0.0.0:8000 miproyecto.wsgi:applicationThis will start Gunicorn on the same interface on which the Django development server runs. You can go back and test the application again. Note that the administration interface will not have any styling applied, since Gunicorn doesn't know about the static content responsible for this.
You've passed Gunicorn a module by specifying the relative path of Django's wsgi.py file, which is the entry point to our application, using Python module syntax. Inside this file, a function named “application” is defined, which is used to communicate with the application.
When you've finished testing, press “CTRL-C” in your server's console window to stop Gunicorn.
You've now finished configuring the Django application. You can exit the virtual environment by typing: “deactivate”
Create a Gunicorn systemd service file
You've already tested that Gunicorn can interact with the Django application, but you need to implement a more robust way to start and stop the application server. To achieve this, you must create a systemd service file.
Create and open a systemd service file for Gunicorn with sudo privileges using a text editor:
# sudo vi /etc/systemd/system/gunicorn.serviceStart with the [Unit] section, which is used to specify metadata and dependencies. Put a description of your service here and tell the init system to start it only after the network target has been reached:
[Unit]
Description=gunicorn daemon
After=network.targetNext, open the [Service] section. Here we'll specify the user and group we want the process to run under. Specify your regular login user, since it owns all the relevant files. For the group, specify “www-data” so Nginx can communicate easily with Gunicorn.
Next, you'll need to map the working directory and specify the command you'll use to start the service. In this case, you must specify the full path to the Gunicorn executable, which is installed inside the virtual environment. You must bind it to the Unix socket inside the project directory, since Nginx is installed on the same server. This is more secure and faster than using a network port. You can also specify any optional Gunicorn settings here. For example, below we've specified 3 worker processes:
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=finn
Group=www-data
WorkingDirectory=/home/finn/miproyecto
ExecStart=/home/finn/miproyecto/miproyectoentorno/bin/gunicorn --workers 3 --bind unix:/home/finn/miproyecto/miproyecto.sock miproyecto.wsgi:applicationFinally, add the [Install] section. This will tell the system what to link the service to if you can start it at boot. This service should start when the regular multi-user system is up and running:
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=finn
Group=www-data
WorkingDirectory=/home/finn/miproyecto
ExecStart=/home/finn/miproyecto/miproyectoentorno/bin/gunicorn --workers 3 --bind unix:/home/finn/miproyecto/miproyecto.sock miproyecto.wsgi:application
[Install]
WantedBy=multi-user.targetWith this, your systemd service file is complete. Save and close it. Now you can start the Gunicorn service you created and enabled to start at boot:
# sudo systemctl start gunicorn && sudo systemctl enable gunicornConfigure Nginx to Proxy Pass to Gunicorn
Now that Gunicorn is configured, you'll need to configure Nginx to pass traffic to the process.
Start by creating and opening a new server block in the Nginx sites-available directory:
# sudo vi /etc/nginx/sites-available/miproyectoInside the file, open a new server block. Start by specifying that this block should listen on port 80 and respond to our server's domain name or IP address:
server {
listen 80;
server_name server_domain_or_IP;
}Next, you need to tell Nginx to ignore any problems finding a favicon. You must also indicate where to find the static files you saved in the ~/miproyecto/static directory. All these files have a standard URI prefix of “/static”, so you can create a location block to match those requests:
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/finn/miproyecto;
}
}Finally, create a location / {} block to match all our other requests. Within this location, we'll include the standard proxy_params file included with the Nginx installation, and then pass traffic to the socket 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/ {
root /home/finn/miproyecto;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/finn/miproyecto/miproyecto.sock;
}
}Save and close the file when you're finished. You can now enable the file by linking it to the sites-enabled directory:
# sudo ln -s /etc/nginx/sites-available/miproyecto /etc/nginx/sites-enabledTest your Nginx configuration for syntax errors with the command:
# sudo nginx -tIf no errors appear, restart Nginx:
# sudo systemctl restart nginxFinally, you need to open the server's firewall for regular traffic on port 80 if this hasn't been done. Since we no longer need access to the development server, you can remove the rule for port 8000 you created earlier.
You should now be able to go to your domain or your Server's IP and see your application.