Setting up Django in Cloudpanel

A walkthrough on how you can set up website made with the Django Framework on cloudpanel using WSGI.

We will be using [[Django]] in this example. The first thing you will want to do is making sure that Gunicorn is installed by typing sudo apt install gunicorn Then, we can check if Gunicorn can serve the application. To do this, we write the following commands:

cd ~/myprojectdir
gunicorn --bind 0.0.0.0:8000 myproject.wsgi

[!NOTE] Note on Virtual Environments If you are running your Django application within a virtual environment, you will first need to make sure you install Gunicorn inside your venv. Starting by activating your environment source venv/bin/activate and running python3 -m pip install gunicorn to add Gunicorn to the venv directory. Then, to test Gunicorn, you write venv/bingunicorn --bind 0.0.0.0:8000 myproject.wsgi

Either codes should give you something similar to this:

[2024-09-20 08:08:27 +0000] [168838] [INFO] Starting gunicorn 23.0.0
[2024-09-20 08:08:27 +0000] [168838] [INFO] Listening at: http://0.0.0.0:8000 (168838)
[2024-09-20 08:08:27 +0000] [168838] [INFO] Using worker: sync
[2024-09-20 08:08:27 +0000] [168839] [INFO] Booting worker with pid: 168839

This test is an important one and has the potential of saving you a lot of time troubleshooting later in the configuration. At later stages, there would be so many interconnected parts, and it would be very difficult to troubleshoot just this one.

Setting up sockets & service files for Gunicorn

Now we need a robust way of starting and stopping the application server, you will create the [[systemd]] [[socket]] and [[service]] files.

To start off, we will create the socket file:

sudo nano /etc/systemd/system/[yourwebsite].socket

And configure the socket in this manner:

[Unit]
Description=[yourwebsite] socket

[Socket]
ListenStream=/run/[yourwebsite].sock

[Install]
WantedBy=sockets.target

Making the service file:

sudo nano /etc/systemd/system/[yourwebsite].service

And configuring it like this:

[Unit]
Description=[yourwebsite] daemon
Requires=[yourwebsite].socket
After=network.target

Next up, we will configure the user in which this process will run under, as well as giving the ownership of [[www-data]] so [[Nginx]] can communicate easily with Gunicorn.

[Unit]
Description=[yourwebsite] daemon
Requires=[yourwebsite].socket
After=network.target

[Service]
User=[yourwebsiteuser]
Group=www-data
WorkingDirectory=/home/[yourwebsiteuser]/htdocs/[yourwebsite]
ExecStart=/home/[yourwebsiteuser]/htdocs/[yourwebste]/env/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          [yourwebsiteproject].wsgi:application

Lastly, we add the [Install]section. This will tell [[systemd]] what to link this service to if you want to enable it to start at boot. We want this service to start when the regular [[multi-user]] system is up and running.

[Unit]
Description=[yourwebsite] daemon
Requires=[yourwebsite].socket
After=network.target

[Service]
User=[yourwebsiteuser]
Group=www-data
WorkingDirectory=/home/[yourwebsiteuser]/htdocs/[yourwebsite]
ExecStart=/home/[yourwebsiteuser]/htdocs/[yourwebste]/env/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/[yourwebsite].sock \
          [yourwebsiteproject].wsgi:application

[Install]
WantedBy=multi-user.target

Now we can close and save this service file.

Now you can start and enable the Gunicorn socket:

sudo systemctl daemon-reload
sudo systemctl restart [yourwebsite]

Check the status of the process to see if it was able to start:

sudo systemctl status [yourwebsite].socket

Next, check for the existence of the [yourwebsite].sock file within the /run directory:

file /run/[yourwebsite].sock

Configuring Nginx to pass proxy to Gunicorn

Now that Gunicorn is configured, we need Nginx to pass HTTP responses to Gunicorn so it can forward the request to the Python application.

Cloudpanel offers a template for Python website, so wyou will only need to add this to the code:

include proxy_params;
        proxy_pass http://unix:/run/[yourwebsite].sock;
server {
  listen 80;
  listen [::]:80;
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  {{ssl_certificate_key}}
  {{ssl_certificate}}
  {{server_name}}
  {{root}}

  {{nginx_access_log}}
  {{nginx_error_log}}

  if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
  }

  location ~ /.well-known {
    auth_basic off;
    allow all;
  }

  {{settings}}

  index index.html;

  location /uwsgi {
    include uwsgi_params;
    uwsgi_read_timeout 3600;
    #uwsgi_pass unix:///run/uwsgi/app/weblate/socket;
    uwsgi_pass 127.0.0.1:{{app_port}};
  }

  location / {
    include proxy_params;
    proxy_pass http://unix:/run/[yourwebsite].sock;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_pass_request_headers on;
    proxy_max_temp_file_size 0;
    proxy_connect_timeout 900;
    proxy_send_timeout 900;
    proxy_read_timeout 900;
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;
    proxy_temp_file_write_size 256k;
  }

  location ~* ^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf)$ {
    add_header Access-Control-Allow-Origin "*";
    expires max;
    access_log on;
  }

  if (-f $request_filename) {
    break;
  }
}

Test your Nginx configuration by typing:

sudo nginx -t

Then restart Nginx:

sudo systemctl restart nginx