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
- You need a Django website ready to be shipped to production.
- Buy a VPS server
- Buy your domain name


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.
sudo apt update && sudo apt upgrade -y
Then let's type in the following command:
sudo apt install -y \
python3-dev \
python3-venv \
python3-pip \
python3-wheel \
libpq-dev \
postgresql \
postgresql-contrib \
nginx \
curl \
git \
ufw
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.
Need a Custom Website or Web App?
We build fast, SEO-optimised, and scalable digital products for businesses and startups. Start today — no commitment required.
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'
ConclusionAt 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.