Deploying a Django Project, Part 5: Launch Your Project with Gunicorn and NGINX - Practicality Beats Purity

Deploying a Django Project, Part 5: Launch Your Project with Gunicorn and NGINX

If you have been following along with this tutorial series, you now have your Django project set up and ready to launch on your Droplet on Digital Ocean. Your project can communicate with your PostgreSQL database, and your project settings are fully configured for launch on your custom domain. In Part 5, we will launch your web application using Gunicorn to handle multiple instances of Django, and we will use NGINX to manage HTTP requests and serve static files. By the end of this tutorial, your project will be live on your custom domain!

 

Test the Project with Gunicorn

In Part 2, you set up a UFW firewall to secure access to your server. You should now allow access to port 8000, which we will use to test the server's ability to serve the project. Allow access through port 8000 with the following command. You may have to input your administrative password.

$ sudo ufw allow 8000

With port 8000 now allowed, you can run the following command to test your project:

$ python manage.py runserver 0.0.0.0:8000

If you have already set up your custom domain name (see Part 1), you should now be able to access your website in a browser by typing in the custom domain name, followed by :8000. For example, http://yourcustomdomainname.com:8000. For this to work, you must have your custom domain name included in the ALLOWED_HOSTS setting in your project's settings.py file (see Part 3).

If you have not yet set up your custom domain, then you will have to enter your Droplet's IP address followed by :8000. For example, 123.45.678.910:8000. For this to work, you must have your IP address included as a string in the ALLOWED_HOSTS setting in your project's settings.py file (see Part 3).

In your browser, you should see your Django web application with most of its functionality. However, depending on your static files setup, your styling and static files may not appear. Don't panic, they should appear when we set up NGINX further along. Also don't close the browser window just yet.

You can also test Gunicorn by cding into your root project directory and running your project with Gunicorn with the following commands. Make sure to replace example_project with your own project's name.

$ cd ~/example_project
$ gunicorn --bind 0.0.0.0:8000 example_project.wsgi

This will run your project with Gunicorn on the same port 8000 as before. Jump back into your browser and perform a hard refresh on your website by typing Ctrl + Shift + R for Windows or Cmd + Shift + R for Mac. It should look the same as before. Again, don't worry if your static files (including your CSS styling) are not working yet.

When you are done checking your website, hit Ctrl + C in the server's terminal window to stop Gunicorn.

Go ahead and deactivate your virtual environment as well by typing:

$ deactivate

 

Setting Up Gunicorn

Gunicorn is essentially a utility (a WSGI application server) that manages communication between your web server (NGINX) and your web application (your Django project). It also manages running multiple instances of your web application at once. Now that you've tested Gunicorn's ability to work with your Django project, you will need to set it up properly by creating systemd socket and service files.

First create a systemd socket file for Gunicorn and open it for editing by typing:

$ sudo nano /etc/systemd/system/gunicorn.socket

With the file open in the nano editor, paste in the following code:

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Save and close the file by typing Ctrl + Xto save, and Enter

Now, create a systemd service file for Gunicorn and open it for editing by typing:

$ sudo nano /etc/systemd/system/gunicorn.service

With the file open in the nano editor, paste in the following code. Make sure you carefully replace all instances of example_userexample_project, and example_env with your own usernameproject name, and virtual environment name

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

[Service]
User=example_user
Group=www-data
WorkingDirectory=/home/example_user/example_project
ExecStart=/home/example_user/example_project/example_env/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          example_project.wsgi:application

[Install]
WantedBy=multi-user.target

Save and close the file by typing Ctrl + Xto save, and Enter.

Now, start and enable the Gunicorn socket by entering the following commands:

$ sudo systemctl start gunicorn.socket
$ sudo systemctl enable gunicorn.socket

Now, check the status of gunicorn.socket to make sure it started without any problems:

$ sudo systemctl status gunicorn.socket

You should receive output, similar to the following, that gunicorn.socket is active and listening:

gunicorn.socket - gunicorn socket
     Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor prese>
     Active: active (listening) since Fri 2020-06-26 17:53:10 UTC; 14s ago
   Triggers: ● gunicorn.service
     Listen: /run/gunicorn.sock (Stream)
      Tasks: 0 (limit: 1137)
     Memory: 0B
     CGroup: /system.slice/gunicorn.socket

You should also check that the gunicorn.sock file was created in the /run/ directory:

$ file /run/gunicorn.sock

You should receive the following output:

/run/gunicorn.sock: socket

In case this process gives you any problems, you can check logs for gunicorn.socket:

$ sudo journalctl -u gunicorn.socket

If you experience problems, these logs may contain error messages that will help you.

Now type the following command to verify that gunicorn.service is still inactive:

$ sudo systemctl status gunicorn

You should receive output similar to the following:

gunicorn.service - gunicorn daemon
   Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
   Active: inactive (dead)

You can test that the socket is working correctly by sending a connection to it with curl:

$ curl --unix-socket /run/gunicorn.sock localhost

You should receive some type of HTML output. Now check that gunicorn.service is running with this command:

$ sudo systemctl status gunicorn

You should now receive output indicating that gunicorn.service is active and running. You will also see a log of connections.

gunicorn.service - gunicorn daemon
     Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled)
     Active: active (running) since Fri 2020-06-26 18:52:21 UTC; 2s ago
TriggeredBy: ● gunicorn.socket
   Main PID: 22914 (gunicorn)
      Tasks: 4 (limit: 1137)
     Memory: 89.1M
     CGroup: /system.slice/gunicorn.service

If any of this gives you problems, check the Gunicorn logs by typing:

$ sudo journalctl -u gunicorn

These logs may contain error messages that will help you debug the issue. If, in resolving any problem, you make changes to your gunicorn.service file, you will need to reload the daemon and restart Gunicorn by typing the following two commands:

$ sudo systemctl daemon-reload
$ sudo systemctl restart gunicorn

Once everything is working properly, you can continue to set up NGINX.

 

Setting up NGINX

With Gunicorn active and ready, you will need to set up NGINX to pass traffic to it. First, create a new server block in NGINX and open it for editing with the following command. Remember to replace example_project with your own project's name.

$ sudo nano /etc/nginx/sites-available/example_project

With the file open in the nano editor, paste in the following code. Make sure to replace yourcustomdomainname.comexample_user, and example_project with your own domain nameusername, and project name. If you have not yet set up your custom domain name, or the nameservers are not yet pointing toward Digital Ocean, you can replace the domain names below (after server_name) with your IP address. Just make sure to go back and change this when you set up your custom domain later.

server {
    listen 80;
    server_name yourcustomdomainname.com www.yourcustomdomainname.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/example_user/example_project;
    }

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

Save and close the file by typing Ctrl + Xto save, and Enter.

Now you should link the file you just created in the sites-available directory to the sites-enabled directory to enable it. Note the space between the two filepaths in the command below:

$ sudo ln -s /etc/nginx/sites-available/example_project /etc/nginx/sites-enabled

You can also make sure your file has no syntax errors with the following command:

sudo nginx -t

If the output shows that there are no errors, restart NGINX with the following command:

$ sudo systemctl restart nginx

Now you will need to update your UFW firewall to allow port 80, which handles normal HTTP connections. You can also remove our exception for port 8000, which you no longer need.

$ sudo ufw delete allow 8000
$ sudo ufw allow 'Nginx Full'

You should now be able to access your full website, with styling and all static files, by entering your custom domain name in the web browser. If you haven't set up your custom domain name yet, you should be able to access your site with your Droplet's IP address.

Congratulations! Your Django project is now live on the internet for anyone to enjoy! In Part 6, we will secure traffic to our server by using TLS encryption and we will get an SSL/TLS certificate for our site. You can find Part 6 here.

-------------------------------

Full Deploying a Django Project tutorial series:

Part 1: Creating and Managing a Custom Domain Name

Part 2: Setting Up the Server

Part 3: Configuring Project Settings and Creating a PostgreSQL Database

Part 4: Clone Your Project onto the Server

Part 5: Launch Your Project with Gunicorn and NGINX

Part 6: SSL/TLS Certificate and Continuing Development

 

Additional resources:

https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-20-04

https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04

https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-20-04

Comments
No comments yet... Add a comment