Managing multiple Django projects on one VPS with Nginx and Gunicorn

WebDev October 21, 2024
On this page
  1. Setting up Gunicorn
  2. Configuring Nginx
  3. Final Commands

A developer often has multiple Django projects, and deploying each one on a separate server isn't efficient. There's usually not enough traffic or CPU load to justify it. Therefore, a single VPS server can suffice, which is also much cheaper. However, beginners might face some difficulties, as setting up one server for multiple projects is slightly more complicated than for just one. Moreover, online resources usually focus on deploying a single project, and there is little information about handling multiple projects.

In this article, we’ll look at how to handle this scenario using a Django + Nginx + Gunicorn stack. We'll use an Ubuntu server as the VPS host. To focus on the main topic, we'll skip the initial server setup. Let's assume that you've already created a new user, under which you’ll be working on the server, and that all necessary packages are installed. For example, the following packages might be installed:

sudo apt install python3-dev libpq-dev postgresql postgresql-contrib nginx curl
sudo pip install pipenv

In short, the deployment process for two independent Django projects involves creating or cloning each project from GitHub into its own directory. Then, for each project, a virtual environment is activated. For this, pipenv might be used. The required version of Gunicorn is installed within each project's virtual environment. Yes, Gunicorn is installed and configured separately for each project. Then, Nginx is configured, which is installed globally, and it determines which Gunicorn socket to forward the request to, based on the site (application) that the user is accessing. Now, let’s look at the setup process in detail with code examples.

Setting up Gunicorn

For each project, you need to create two files: gunicorn.socket and gunicorn.service.

Create the gunicorn.socket file for the first project:

sudo nano /etc/systemd/system/gunicorn-project-1.socket

Contents of the file:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn-project-1.sock

[Install]
WantedBy=sockets.target

Create the gunicorn.service file for the first project:

sudo nano /etc/systemd/system/gunicorn-project-1.service

Contents of the file:

[Unit]
Description=gunicorn daemon for project 1
Requires=gunicorn-project-1.socket
After=network.target

[Service]
User=itw_user
Group=www-data
WorkingDirectory=/home/some_user/project-1
ExecStart=/home/some_user/.local/share/virtualenvs/project-1-cVjed2Wi/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn-project-1.sock \
core.wsgi:application

[Install]
WantedBy=multi-user.target

Here:

  • ExecStart=/home/some_user/.local/share/virtualenvs/project-1-cVjed2Wi/bin/gunicorn – this is the path to the virtual environment where Gunicorn is installed (using pipenv).
  • core.wsgi:application – this points to your Django project's root directory (where settings.py is located).

Next, run the following commands:

sudo systemctl daemon-reload
sudo systemctl start gunicorn-project-1.socket
sudo systemctl enable gunicorn-project-1.socket

You need to perform the same steps for the second project project-2, replacing project-1 with project-2 in all instances. After this, both Gunicorn services will be running on your server.

Configuring Nginx

Now you need to configure Nginx to handle requests. It’s straightforward. We need to create two configuration files for project-1 and project-2, each specifying the appropriate socket in the location / section:

location / {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn-project.sock;
}

Example Nginx configuration file for project-1 listening on port 80:

sudo nano /etc/nginx/sites-available/project-1

Contents of the file:

server {
    listen 80;
    server_name project-1.com;  # Replace with the first project's domain

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn-project-1.sock;  # Use the project-1 Gunicorn socket
    }
}


To create a symlink for enabling this configuration:

sudo ln -s /etc/nginx/sites-available/project-1 /etc/nginx/sites-enabled/

Example Nginx configuration file for project-2:

sudo nano /etc/nginx/sites-available/project-2

Contents of the file:

server {
    listen 80;
    server_name project-2.com;  # Replace with the first project's domain

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn-project-2.sock;  # Use the project-2 Gunicorn socket
    }
}


To create a symlink for enabling this configuration:

sudo ln -s /etc/nginx/sites-available/project-2 /etc/nginx/sites-enabled/

Final Commands

sudo systemctl daemon-reload
sudo systemctl start gunicorn-project-1.socket
sudo systemctl enable gunicorn-project-1.socket
sudo systemctl start gunicorn-project-2.service
sudo systemctl enable gunicorn-project-2.service
sudo systemctl restart nginx

This way, both of your Django projects will be running on the same server at project-1.com and project-2.com.

The SSL certificate setup is done as usual and the number of projects running on a single host doesn't affect this process.