How to Set Up Django with PostgreSQL, Nginx, and Gunicorn on Ubuntu VPS Server

Last updated 2 weeks, 5 days ago · 11 min read

Welcome! Today, we will discuss how you can host/deploy your Django website on any VPS server. First of all, before thinking about hosting your website, it's important to know that the website we are hosting today is built using Django. We have already developed our Django website, and we are going to be hosting it on a VPS server. We will set it up to work with a PostgreSQL Database, Nginx server, and Gunicorn on Ubuntu 24.04 (LTS). This will also work on Debian.

What you need before hosting your website

  1. You need a Django website ready to be shipped to production.
  2. Buy a VPS server
  3. Buy your domain name
If you have yet to get your domain name, you can use Dynadot Domains or NameCheap Domains. I will be using Dynadot and Namecheap for this tutorial as I am affiliated with them. And if you don't have your server yet, in this tutorial, we're going to be hosting our website on Linode. However, there are other options like DigitalOcean, Vultr , Namecheap VPS , and even Google Cloud. There are several other great cloud providers, but today, we will walk you through the process using Linode. If you're unsure which VPS server to choose, I recommend checking out our detailed guide on the top 5 VPS servers. Explore the best and most affordable options here - Top 5 VPS Servers. The same steps can be applied on DigitalOcean, Namecheap VPS, Vultr, or any VPS server that provides root access and an IP address. So, without wasting much time, let's dive right in! So, firstly, make sure you have your Django website in a file and keep it organized in a separate folder. Also, have your domain name written down somewhere for easy reference. Now, go to the Linode and create an account. Once you are on your dashboard, click on the Linode and create Linode. Check the pictures below. Select your server location, OS, and OS version, in our case Ubuntu 24.04. Select your Linode plan depending on your use case. Add a label and password for your Linode. Check and recheck everything and scroll down to create Linode. Wait until it's fully booted, you will get an IP address dedicated to your server. Next, grab your new IP address and remember your password. There's also an option to use an SSH key to access your server. However, in this tutorial, we're going to be using our password for simplicity. You can change to SSH keys anytime you want. Our servers are called "Linodes or nanodes", on Linode on Vultr they are called "Cloud Compute, Virtual Machines (VMs) & Servers", and on DigitalOcean, the servers are called "Droplets," while on other platforms can call them VPS servers, Cloud VPS, etc. If you are using Digital Ocean, go to the DigitalOcean create page for your Droplet. This is where you'll set up your server. The same thing applies to other platforms, follow their steps until you have your IP address and password.

Accessing Our Server With an SSH Client

Now, we need to get an SSH client. Some options include PuTTY, or you can even use your command line or PowerShell. There's also a powerful SSH client called MobaXterm. For this tutorial, we will be using MobaXterm. If you don't have MobileXterm installed on your system, head to their website, to download and install it. Use this link Okay, so once you are done with the installation of MobaXterm. Open your MobaXterm application. Click on the Session button, it will pop up a session setting box. Click on SSH and add your IP address in the Remote host field > Check the Specify Name and add root in the field. Trace the ok button below and click it. You will be prompted to enter the password you used while creating your server. Enter the password and hit Enter. Since this is your first time logging into the server, it will prompt you to... It will prompt you, to ask if you want to connect to this server. You can hit Enter. Now we have successfully logged in to our new server, we need to start installing the relevant tools to run our server. In our case, we are setting up the environment to power our Django website. This website will use Nginx as the web server, PostgreSQL as our database, and Python as our programming language. So, we need to install Python, PostgreSQL, and Nginx on the server. First, we have to update our server, let's copy the code below and run it. sudo apt update Then let's type in the following command: sudo apt install python3-dev python3.12-venv libpq-dev postgresql postgresql-contrib nginx curl Let's wait for a moment while all our software is installed. It's time to create our database, let's run the code below: sudo -u postgres psql Now we will be inside the PostgreSQL database. It's time to create our database. CREATE DATABASE mydb; Don't forget to end all the SQL statements with a semicolon, please sir/ma. We are supposed to create a database user to connect with Django. But we have to act a little lazy lol. This is because we already have a super user with the name "postgres", we can just set up a password for our user as it already has all the needed permissions, we can just get things done asap. What we will do is create a password for our PostgreSQL user and use that user as our database user for the Django app. Okay, let's type \password and it will prompt us to add a password for the PostgreSQL user. (No semicolon this time) We enter our desired password, confirm it by entering it again, and then hit Enter. Now, our PostgreSQL user has a password.
Now our database user is "postgres" and our password is the new password we created.
Okay, so now it's time to create our Django project. We already have our Django project locally. In MobaXterm, you can't directly upload a folder.
So, let's start the Django project again here.
We need to move to the root directory of our Ubuntu server. Then, create a home folder if it doesn't already exist. cd /.. Will move use to the root directory. mkdir home to create our home folder.

Note: the command cd (change directory) is used to navigate the command line interface. If you want to move one-inch backward use "cd ..", to move frontward use cd then the name of the available folder Ex: "cd home" or "cd home/mywebsite", but make sure the folder(s) exist there before trying to move to it. You can check the available folders with "ls" command. You can also move to the root directory one touch using the "cd /.." command.

Now we have created our home and project folder, let's create our virtual environment. python3 -m venv myvitname
Let's wait for a moment. Once the virtual environment is created, we need to activate it. source myvitname/bin/activate
Once we've activated our virtual environment, it's time to install some packages. We need Django, Gunicorn, Psycopg, pillow, etc. pip install django gunicorn psycopg[binary] pillow Now is the time to create our Django project. django-admin startproject mynewproject Once you have created your Django project, move straight to the settings.py and open it.
MobaXterm has a file editor, you can use that to edit your files or right-click on the file and select "open with" It will show you multiple options, and you can select VS code.
Now, remember your domain name and your IP address. In the settings.py file, you'll find the "ALLOWED_HOSTS" section. You may have omitted this in your local development environment, but now it's time to fill that in for your Django project.
In the "ALLOWED_HOSTS," add the domain name you bought from Dynadot or Namecheap. Include both the www version and the non-www version. Our domain will not work yet, but let's have them there, for now, only the IP address will work at this point.
Then, copy the IP address from your server and add it to the list. If you fail to add all of them to the list Django will not allow them to serve your website. So we also need to add localhost to our list since our PostgreSQL database is available on our local server.
This is how our ALLOWED_HOST will look like: ALLOWED_HOSTS = ['18.28.00.15', 'withubb.com', 'www.withubb.com', 'localhost'] Let's move down to the place where we have the database settings, it has sqlite3 database by default change it to our PostgreSQL database. DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": "mydatabase", "USER": "mydatabaseuser", "PASSWORD": "mypassword", "HOST": "localhost", "PORT": "", } } I highly recommend you use environment variables to store your secret key, passwords, and other sensitive information.
Scroll down to where we have the media and static root and add the configuration below so Django can serve your static and media root. STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'static/' MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'mediafiles/' Run the following commands to create the initial migrations and apply them to your PostgreSQL database: python manage.py makemigrations python manage.py migrate To create an admin user, run: python manage.py createsuperuser You will be prompted to enter a username, email, and password. Gather all static content by running: python manage.py collectstatic Confirm the operation when prompted, (by typing yes) and the static files will be placed in the `static` directory.
To test the development server securely, you need to configure your firewall. The commands below allow traffic on port 8000 and enable the firewall: sudo ufw allow 8000 sudo ufw enable Let's view our website online, start your development server with the following command: python manage.py runserver 0.0.0.0:8000 Visit your server's IP or domain followed by :8000 (EX: your.server.ip.address:8000). For the admin interface, append /admin to the URL and log in using your admin credentials.

Testing Gunicorn

To test Gunicorn’s ability to serve your Django project, navigate to your project directory where manage.py file is located and run: gunicorn --bind 0.0.0.0:8000 mywebsite.wsgi This helps ensure Gunicorn is properly configured before automating its startup to serve our webapp.
Note: The admin interface will not have styling at this point, as Gunicorn doesn't serve static files. Press CTRL+C to stop the server.
Exit your virtual environment by typing: deactivate

Automating Gunicorn

To automate Gunicorn startup, create a systemd socket and service. Run this command to create and open your socket file in editing mode: sudo nano /etc/systemd/system/gunicorn.socket Add the following: [Unit] Description=gunicorn socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target This allows systemd to manage socket-based communication for our application.
Next, create the service file: sudo nano /etc/systemd/system/gunicorn.service Add this content: [Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target [Service] User=root Group=www-data WorkingDirectory=/home/mywebsite/ ExecStart=/home/mywebsite/myvitname/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/run/gunicorn.sock \ mywebsite.wsgi:application [Install] WantedBy=multi-user.target Configures systemd to manage Gunicorn as a service and ensures that it starts automatically and reliably on boot.
Enable and start the Gunicorn socket: sudo systemctl start gunicorn.socket sudo systemctl enable gunicorn.socket Verify the gunicorn socket is active: sudo systemctl status gunicorn.socket If you encounter any error, it may be from your service file. Make sure ExecStart= line is pointing to where your installed gunicorn is and the WorkingDirectory= is pointing to your Django project. Once all this is fixed, gunicorn should work fine.

Configuring Nginx as a Reverse Proxy

To use Nginx as a reverse proxy and serve our website, create a server block configuration.: sudo nano /etc/nginx/sites-available/mywebsite Add the following configuration: server { server_name mywebsite.com, www.mywebsite.com, my_server_IP; location = /favicon.ico { access_log off; log_not_found off; } location /static/ { root /home/mywebsite/mywebsite ; } location / { include proxy_params; proxy_pass http://unix:/run/gunicorn.sock; } } Edit the file and replace the mywebsite.com with your website domain and the my_server_IP with your actual server IP address. And in the location /static/ replace the root to point to your Django project. Create a symbolic link file and enable the configuration: sudo ln -s /etc/nginx/sites-available/mywebsite /etc/nginx/sites-enabled sudo nginx -t sudo systemctl restart nginx The sudo ln -s command creates a symbolic link from /etc/nginx/sites-available/mywebsite to /etc/nginx/sites-enabled, effectively enabling the configuration. Like automatically mirroring and exchanging file data with the liked file. The systemctl restart nginx restarts our nginx server. While nginx -t tests our nginx configurations.

Troubleshooting Nginx Errors

If Nginx encounters errors, follow these steps to troubleshoot and fix them:
Step 1: Check Configuration Syntax Run: sudo nginx -t If you get an error, there's a mistake in the configuration file.
Step 2: Review Logs Check the error log for detailed information: sudo tail -f /var/log/nginx/error.log This will display and identify specific issues causing errors. Check and fix the error.
Step 3: Verify File Paths and Permissions Ensure that static files and socket paths are correct and accessible: sudo ls -l /home/mywebsite/mywebsite sudo ls -l /run/gunicorn.sock The ls -l will list directory and file permissions. This helps ensure that Nginx can access the required files and the Gunicorn socket.

Then restart Nginx, Daemon, and Gunicorn services. sudo systemctl restart nginx sudo systemctl restart gunicorn sudo systemctl daemon-reload And anytime you make changes to your Nginx files, you must repeat all the above code to pick the changes. But if you made changes to your Django project you don't need to restart Nginx, you can only restart gunicorn and reload the daemon.

Firewall Configuration

Check the firewall status: sudo ufw status If inactive, enable it and add rules to avoid unauthorized access or locking yourself out: sudo ufw enable When prompted, input Y and press Enter.
Run the following commands: sudo ufw delete allow 8000 sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' Conclusion
At this stage, your application should be live and accessible via your server's domain or IP address. You've done an amazing job following along! In the next chapter, we'll cover advanced Nginx configurations, securing your website with Certbot's free SSL certificate, and setting up DNS records for your domain name.

More Articles

Mastering SQL Queries: A Beginner’s Guide

SQL Queries: A comprehensive guide for beginners

1 week, 3 days ago · 9 min read
Withubb Web and IT Solution

What is Withubb Ltd? ( Withubb web and IT services, features and benefits)

1 week, 5 days ago · 8 min read
Creating a landing page for your business

Landing Page Best Practices for High Conversion Rates

1 month ago · 8 min read
The Importance of Domain Extensions: .com Vs .net Vs .org Vs .ng

.org, .com and .net Choosing the right domain extensions

1 month ago · 6 min read
How to Become a Full Stack Developer: Essential Skills You Need

Skills to Become a Full Stack Developer in 2025

1 month, 1 week ago · 11 min read
Choosing the Right Database for Your Business Website

How to Choose the right Database for Your Business Website

1 month, 1 week ago · 6 min read
Common Programming Mistakes and How to Avoid Them

Avoiding common programming mistakes: best practices for quality code

1 month, 2 weeks ago · 16 min read
A Comprehensive Guide to Domain Parking and Leveraging It for Passive Income

What is Domain parking and how to earn money from it ?

1 month, 2 weeks ago · 8 min read
Essential Elements for Building an SEO-Friendly Website

Steps to Building an SEO-Friendly Website

1 month, 3 weeks ago · 7 min read
The Ultimate guide to building a stunning website: a step by step guide

How to build a stunning website (A step by step guide)

1 month, 3 weeks ago · 12 min read