Let's Start with the Basics: What is Django?
Django is a web framework that can help you get a Python application or your website off the ground. Django includes a simplified development server for local code testing, but for any production service, a more secure and powerful web server is required.
In this post, we will explain how to install and configure some components on Ubuntu 24.04 to support and serve Django applications. On one side, we will create a PostgreSQL database and configure the Gunicorn application server to interact with our applications. Then, we will set up Nginx as a reverse proxy for Gunicorn, which will give us access to its security and performance features for serving 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 🙂
Requirements
- A VPS Cloud server running Ubuntu 24.04.
- A non-root user with "sudo" privileges and 751 permissions on that user's home directory. In this example, we use a "sudo" user named finn.
Install Packages from Ubuntu Repositories
To start the process, you will need to download and install all the necessary elements from the Ubuntu repositories. Then, we will also use Python’s pip package manager to install additional components.
To do this, you need to update the package index and then download and install the required packages.
$ sudo apt update
$ sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx -y
This will install pip, the necessary Python development files for installing Gunicorn later, the Postgres database system and the necessary libraries to interact with it, and the Nginx web server.
Create the PostgreSQL Database and User
Now let's create the database and the database user for our Django application.
By default, PostgreSQL uses an authentication scheme called "peer authentication" for local connections. Essentially, this means that if the operating system username matches a valid Postgres username, this user can log in without additional authentication.
During the installation of Postgres, an operating system user named postgres is created to correspond to the PostgreSQL administrative user. You need to 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 psql
Now you will see a PostgreSQL prompt where you can set up your requirements.
First, create a database for your project:
postgres=# CREATE DATABASE myproject;
postgres=# CREATE USER usuariomyproject WITH PASSWORD 'password';
Next, you need to modify some of the connection parameters for the user you just created. This will speed up database operations so that the correct values do not have to be queried and configured each time a connection is established.
You need to 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 reading uncommitted transactions. Finally, you’ll need to set the timezone.
By default, Django projects are set to use UTC. These are all the recommendations from the Django project itself:
postgres=# ALTER ROLE usuariomyproject SET client_encoding TO 'utf8';
postgres=# ALTER ROLE usuariomyproject SET default_transaction_isolation TO 'read committed';
postgres=# ALTER ROLE usuariomyproject SET timezone TO 'UTC';
Now you can grant your new user access to manage your new database:
postgres=# ALTER DATABASE myproject OWNER TO usuariomyproject;
When you’re done, 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 rest of the requirements needed to get the project ready. We’ll install the Python requirements inside a virtual environment to make management easier.
First, install the Python virtual environment package with the following command:
$ sudo apt install python3-venv -y
Next, create the directory where you will store your project files, and navigate to it:
$ mkdir ~/myproject
$ cd ~/myproject
Within the project directory, create a Python virtual environment with the command:
$ python3 -m venv venv
Then, activate the virtual environment with the following command:
$ source venv/bin/activate
The prompt should change to indicate that you are now operating within a Python virtual environment. It should look something like this:
(venv)user@host:~/myproject$
With the virtual environment active, we will install Django, Gunicorn, and the PostgreSQL psycopg2 adapter with the local pip instance:
(venv)user@host:~/myproject$ pip install django gunicorn psycopg2
Create and Configure a New Django Project
With the Python components installed, you can create the Django project files. Since you already have a project directory, you need to tell Django to install the files there. This will create another directory within the main one with the actual code (which is normal) and place a management script in this directory.
(venv)user@host:~/myproject$ django-admin startproject myproject .
The period at the end tells django-admin to create the project in the current directory.
Adjust Project Settings
The first thing you need to do with your newly created project files is adjust the settings. Open the configuration file with a text editor:
(venv)user@host:~/myproject$ nano myproject/settings.py
First, find the section that configures the database access. It will start with DATABASES. The configuration in the file is for a SQLite database. Since we have created a PostgreSQL database for our project, we need to adjust the configuration.
Change the configuration to use the PostgreSQL database information. Then, you need to tell Django to use the psycopg2 adapter you installed with pip. You need to provide the database name, user, and password, and then specify that the database is local. You can leave the PORT value as an empty string, for example in myproject/myproject/settings.py:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myproject',
'USER': 'usermyproject',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
}
In ALLOWED_HOSTS, you need to add the list of IP addresses or domain names that will be allowed to access the Django instance.
ALLOWED_HOSTS = ['server_IP_or_name', 'server_IP_or_name_two', ...]
Next, go to the end of the file and add the path where static files should be stored. This is necessary for Nginx to handle requests for these objects. The following line tells Django to place them in a directory called "static" in the base project directory:
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static/'
Save and close the file when you have finished making the changes.
Complete the Initial Project Setup
Now, we can migrate the initial database schema to our PostgreSQL database using the management script:
(venv)user@host:~/myproject$ cd ~/myproject
(venv)user@host:~/myproject$ ./manage.py makemigrations
(venv)user@host:~/myproject$ ./manage.py migrate
Create an admin user for the project by running:
(venv)user@host:~/myproject$ ./manage.py createsuperuser
You will need to select a username, provide an email address, and choose and confirm a password.
You can collect all the static content in the directory you’ve configured by running:
(venv)user@host:~/myproject$ ./manage.py collectstatic
The static files will be stored in a directory called “static” within the project directory.
To proceed with testing, it is necessary to have port 8000 open on your server. Check if the port is already open for your server, and if not, open it by following this article from our knowledge base:
Manage Firewall Profile Rules from the Client Panel
Once the port is open, you can test the project by starting the Django development server with the command:
(venv)user@host:~/myproject$ ./manage.py runserver 0.0.0.0:8000
To check it, access your domain or server IP through any browser, 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 admin username and password you created earlier with the “createsuperuser” command.
After authenticating, you will have access to the default Django admin interface:
When you’re done exploring the interface, press “CTRL+C” in your server 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 to test Gunicorn to ensure it can serve the application. We can do this easily by running:
$ cd ~/myproject
$ gunicorn –b 0.0.0.0:8000 myproject.wsgi:application
This will start Gunicorn on the same interface where the Django development server runs. You can go back and test the application again. Note that the admin interface will not have any styling applied, as Gunicorn does not handle static content.
You’ve passed Gunicorn a module by specifying the relative path to Django’s wsgi.py file, which is the entry point to our application, using Python module syntax. Inside this file, a function called “application” is defined, which is used to communicate with the application.
When you’re finished testing, press “CTRL-C” in your server console window to stop Gunicorn.
You have now completed the Django application setup. You can exit the virtual environment by running: “deactivate”
Create a Gunicorn systemd Service File
You have verified that Gunicorn can interact with the Django application, but it is necessary to implement a more robust way to start and stop the application server. To achieve this, you should create a systemd service file.
Create a systemd service file for Gunicorn using a text editor:
$ sudo vi /etc/systemd/system/gunicorn.service
Start with the [Unit] section, which is used to specify metadata and dependencies. Provide a description for your service here and tell the init system to start it only after the network target has been reached:
The next step is to open the [Service] section. Here, you will specify the user and group under which you want the process to run. You should use your regular access user since they own all relevant files. For the group, specify “www-data” to ensure Nginx can communicate easily with Gunicorn.
Next, map the working directory and specify the command to start the service. You need to provide the full path to the Gunicorn executable, which is installed within the virtual environment. Link it to a Unix socket within the project directory, as 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, we have specified 3 worker processes:
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=finn
Group=www-data
WorkingDirectory=/home/finn/myproject
ExecStart=/home/finn/myproject/venv/bin/gunicorn --workers 3 --bind unix:/home/finn/myproject/myproject.sock myproject.wsgi:application
[Install]
WantedBy=multi-user.target
With this, your systemd service file is complete. Save and close the file. You can now start the Gunicorn service and enable it to start on boot:
$ sudo systemctl start gunicorn
$ sudo systemctl enable gunicorn
Configure Nginx as a Proxy Pass for Gunicorn
Now that Gunicorn is configured, you need to set up Nginx to pass traffic to the Gunicorn process.
Start by creating and opening a new server block in the Nginx sites-available directory:
$ sudo vi /etc/nginx/sites-available/myproject
Inside the file, open a new server block. Start by specifying that this block should listen on port 80 and respond to your server’s domain name or IP address:
server {
listen 80;
server_name server_domain_or_IP;
}
Next, tell Nginx to ignore any issues finding a favicon. Also, indicate where to find the static files you have saved in the ~/myproject/static directory. All these files have a standard URI prefix “/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/myproject;
}
}
Finally, create a location / {} block to match all other requests. Inside this location block, include the standard proxy_params file included with the Nginx installation, and then pass the traffic to the socket created by our Gunicorn process:
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/finn/myproject;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/finn/myproject/myproject.sock;
}
}
Save and close the file when finished. Now you can enable the file by linking it to the sites-enabled directory:
$ sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
Test your Nginx configuration for syntax errors with the command:
$ sudo nginx -t
If no errors are reported, restart Nginx:
$ sudo systemctl restart nginx
Finally, you need to open the server firewall for regular traffic on port 80 if this hasn't been done already. Since you no longer need access to the development server, you can remove the rule for port 8000 that you created earlier.
You should now be able to visit your domain or server's IP address and see your application.
We hope this article has been helpful. Have you tried it out? Let us know your feedback! 🙂
Remember, if you have any questions about this or other issues related to your servers on Clouding, feel free to reach out to suport@clouding.io We're here to assist you with anything you need!