Comprehensive Django Deployment Guide for Beginners

Saurav SharmaSaurav Sharma
10 min read

Table of Contents

  1. Introduction

  2. Prerequisites

  3. Setting Up Your VPS

  4. Securing Your Server

  5. Installing Required Software

  6. Configuring Your Django Project

  7. Setting Up Nginx and Uvicorn

  8. Configuring SSL with Let's Encrypt

  9. Setting Up Supervisor

  10. Firewall Configuration

  11. Deploying Your Django Application

  12. Running the Redis Scheduler

  13. Monitoring and Maintenance

  14. Troubleshooting

  15. Conclusion

1. Introduction

This guide will walk you through the process of deploying a Django application with ASGI (Uvicorn) to a Virtual Private Server (VPS). We'll also cover setting up a Redis scheduler, configuring Nginx as a reverse proxy, securing your server with a firewall, and enabling HTTPS. This guide is designed for beginners, so we'll explain each step in detail.

2. Prerequisites

Before we begin, make sure you have:

  • A VPS running Ubuntu 22.04

  • A domain name (e.g., tymr.online) pointed to your VPS's IP address

  • Your Django project code (including requirements.txt)

  • Basic familiarity with command-line interfaces

3. Setting Up Your VPS

First, we need to access our VPS. We'll use SSH (Secure Shell) for this.

ssh root@your_server_ip

Replace your_server_ip with your VPS's IP address. You'll be prompted for a password, which you should have received from your VPS provider or in most VPS you can directly login into server after pasting the local desktop’s ssh public key into the server during the setup process. for example, in digital ocean you have this feature here while creating a droplet ( VPS ) 👇

Once logged in, update your system:

sudo apt update
sudo apt upgrade -y

These commands update the list of available packages and upgrade them to their latest versions.

4. Securing Your Server

Creating a New User

It's best practice to avoid using the root user. Let's create a new user:

adduser username

Replace username with your desired username. You'll be prompted to set a password and provide some optional information.

Grant this user sudo privileges:

usermod -aG sudo username

This command adds the user to the sudo group, allowing them to run commands with superuser privileges.

Setting Up SSH Key Authentication

SSH keys are more secure than passwords. On your local machine, generate an SSH key pair:

ssh-keygen -t rsa -b 4096

This creates a public/private key pair. The public key can be freely shared, while the private key must be kept secret.

Copy the public key to your server:

ssh-copy-id username@your_server_ip

Now, let's disable password authentication for SSH. On the server:

sudo nano /etc/ssh/sshd_config

Find and modify these lines:

PasswordAuthentication no
PermitRootLogin no

Save the file (Ctrl+X, then Y, then Enter) and restart the SSH service:

sudo systemctl restart sshd

Now login as non root user you just created.

5. Installing Required Software

Let's install the necessary software:

sudo apt install build-essential python3-dev python3.11 python3.11-venv python3-pip nginx redis

This installs Python 3.11, pip (Python package manager), and Nginx (web server).

6. Configuring Your Django Project

Create a directory for your project:

mkdir ~/myproject
cd ~/myproject

Upload your project files to this directory. You can use SCP, SFTP, or Git for this.

Create a virtual environment:

python3.11 -m venv venv
source venv/bin/activate

Install your project dependencies:

pip install -r requirements.txt

Also, install Uvicorn:

pip install uvicorn

Understanding and Setting Up User Permissions for our Django project

When deploying a Django application, proper file permissions and ownership are crucial for security and functionality. Here's a detailed guide tailored for beginners:

Understanding Users and Groups

In our setup, we'll work with three main entities:

  1. Your user account (e.g., 'normaluser')

  2. The web server user (usually 'www-data' for Nginx on Ubuntu)

  3. The root user

Best Practices for Django Project Files

  1. Project Directory Ownership: Your user should own the project directory and most files.

     sudo chown -R normaluser:normaluser /home/normaluser/myproject
    
  2. Project Directory Permissions: Set 755 (drwxr-xr-x) for directories and 644 (-rw-r--r--) for files.

     find /home/normaluser/myproject -type d -exec chmod 755 {} \;
     find /home/normaluser/myproject -type f -exec chmod 644 {} \;
    
  3. Sensitive Files: For files like settings.py, use more restrictive permissions.

     chmod 600 /home/normaluser/myproject/myproject/settings.py
    
  4. Static and Media Directories: The web server needs read access to these.

     sudo chown -R normaluser:www-data /home/normaluser/myproject/static
     sudo chown -R normaluser:www-data /home/normaluser/myproject/media
     sudo chmod -R 750 /home/normaluser/myproject/static
     sudo chmod -R 750 /home/normaluser/myproject/media
    
  5. Log Directory: Create a logs directory within your project and set appropriate permissions.

     mkdir -p /home/normaluser/myproject/logs
     sudo chown -R normaluser:www-data /home/normaluser/myproject/logs
     sudo chmod -R 770 /home/normaluser/myproject/logs
    
  6. SQLite Database (if used): If you're using SQLite, secure the database file.

     sudo chown normaluser:www-data /home/normaluser/myproject/db.sqlite3
     sudo chmod 660 /home/normaluser/myproject/db.sqlite3
    
  7. Virtual Environment: Keep your virtual environment secure.

     chmod -R 750 /home/normaluser/myproject/venv
    

Explanation of Permissions

  • 755 for directories: Owner can read/write/execute, others can read/execute.

  • 644 for regular files: Owner can read/write, others can read.

  • 600 for sensitive files: Only owner can read/write.

  • 750 for static/media: Owner can read/write/execute, group (www-data) can read/execute.

  • 770 for logs: Owner and group can read/write/execute.

  • 660 for SQLite: Owner and group can read/write.

Security Best Practices

  1. Principle of Least Privilege: Give only the permissions necessary for each file and directory.

  2. Use a Non-Root User: Always run your Django application as a non-root user.

  3. Separate Media Uploads: If possible, store user-uploaded files outside of the project directory.

  4. Regular Audits: Periodically check and update file permissions.

  5. Use Environment Variables: Store sensitive information like secret keys in environment variables, not in files.

  6. Secure the Secret Key: Ensure Django's SECRET_KEY is not in version control and has restricted access.

  7. Backup Permissions: When backing up, preserve file permissions.

Applying Changes

After setting permissions, restart your web server and Django application:

sudo systemctl restart nginx
sudo supervisorctl restart all

Verifying Permissions

You can verify permissions using the ls -l command. For example:

ls -l /home/normaluser/myproject

This will show you the permissions for files and directories in your project root.

Remember, these settings are a starting point. You may need to adjust based on your specific setup and security requirements. Always test thoroughly after changing permissions to ensure your application functions correctly.

7. Nginx Configuration

Before we set up our Nginx configuration, let's remove the default configuration:

sudo rm /etc/nginx/sites-enabled/default

Now, let's create our configuration file:

sudo nano /etc/nginx/sites-available/myproject

Add the following content:

server {
    # replace your domain names with placeholder tymr.online
    server_name tymr.online www.tymr.online;  # Your domain name(s)

    location = /favicon.ico { 
        access_log off; 
        log_not_found off; 
    }  # Efficiently handle favicon requests

    location /static/ {
        root /home/username/myproject;  # Path to your static files
    }  # Serve static files directly

    location / {
        proxy_pass http://unix:/home/username/myproject/myproject.sock;  # Pass requests to Uvicorn
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $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 settings for Uvicorn
}

Let's break this down:

  • server_name: Specifies which domain names this server block should respond to.

  • location = /favicon.ico: Efficiently handles favicon requests.

  • location /static/: Configures Nginx to serve static files directly, without passing the request to Django.

  • location /: The main block that handles all other requests.

    • proxy_pass: Forwards requests to our Uvicorn server.

    • The proxy_set_header directives: Pass necessary headers to the Django application.

After creating this file, enable it:

sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

This creates a symbolic link, effectively enabling our configuration.

Test the Nginx configuration:

sudo nginx -t

If it's okay, restart Nginx:

sudo systemctl restart nginx

8. Setting Up SSL with Let's Encrypt

We'll use Snap to install Certbot, which automates the process of obtaining and renewing Let's Encrypt certificates.

  1. Ensure Snap is up-to-date:

     sudo snap install core; sudo snap refresh core
    

    This installs and updates the core snap package.

  2. Remove any old Certbot installations:

     sudo apt remove certbot
    
  3. Install Certbot via Snap:

     sudo snap install --classic certbot
    

    The --classic flag allows Certbot to access system resources outside of the snap's confined environment.

  4. Create a symbolic link for easy access:

     sudo ln -s /snap/bin/certbot /usr/bin/certbot
    
  5. Obtain and install the certificate:

     sudo certbot --nginx -d tymr.online -d www.tymr.online
    

    This command will:

    • Obtain a certificate for your domains

    • Automatically configure Nginx to use the new certificate

    • Set up automatic renewal

Follow the prompts during the Certbot execution. It will ask for your email and agreement to terms of service.

For more detailed information, you can refer to this similar guide: How To Secure Nginx with Let's Encrypt on Ubuntu 22.04

9. Configuring Supervisor

Supervisor is a process control system that allows you to monitor and control a number of processes on UNIX-like operating systems. We use Supervisor because:

  1. It automatically restarts processes if they crash.

  2. It starts processes on system boot.

  3. It provides an easy way to start, stop, and monitor processes.

  4. It logs stdout and stderr of the processes it manages.

Here's how to set it up:

  1. Install Supervisor:

     sudo apt install supervisor
    
  2. Create a configuration file:

     sudo nano /etc/supervisor/conf.d/myproject.conf
    
  3. Add the following content:

[program:myproject]
command=/home/username/myproject/venv/bin/uvicorn myproject.asgi:application --unix-socket /home/username/myproject/myproject.sock
directory=/home/username/myproject
user=username
autostart=true
autorestart=true
stderr_logfile=/var/log/myproject.err.log
stdout_logfile=/var/log/myproject.out.log

[program:celery]
command=/home/username/myproject/venv/bin/celery -A myproject worker -l info
directory=/home/username/myproject
user=username
autostart=true
autorestart=true
stderr_logfile=/var/log/celery.err.log
stdout_logfile=/var/log/celery.out.log

[program:redis_scheduler]
command=/home/username/myproject/venv/bin/python manage.py redis_scheduler
directory=/home/username/myproject
user=username
autostart=true
autorestart=true
stderr_logfile=/var/log/redis_scheduler.err.log
stdout_logfile=/var/log/redis_scheduler.out.log

This configuration sets up three processes:

  • Your Django application running with Uvicorn

  • A Celery worker for background tasks

  • Your custom Redis scheduler

  1. Reload Supervisor to apply changes:

     sudo supervisorctl reread
     sudo supervisorctl update
    

Supervisor is different from simple systemd services because it provides more fine-grained control and is specifically designed for managing application processes, making it ideal for complex setups like Django projects with multiple components.

10. Firewall Configuration

UFW (Uncomplicated Firewall) is a user-friendly interface for managing iptables, the default firewall management tool for Ubuntu.

Enable UFW:

sudo ufw enable

Allow necessary connections:

sudo ufw allow ssh
sudo ufw allow 'Nginx Full'

These commands allow SSH connections and both HTTP and HTTPS traffic for Nginx.

Check the status:

sudo ufw status

Final List of UFW Commands:

sudo ufw allow OpenSSH # You need to allow SSH so you can access your VPS remotely.
sudo ufw allow 'Nginx HTTP'
sudo ufw allow 'Nginx HTTPS'
sudo ufw allow 8000  # Optional, for development server
sudo ufw enable
sudo ufw status

11. Deploying Your Django Application

Collect static files:

python manage.py collectstatic

Apply migrations:

python manage.py migrate

12. Running the Redis Scheduler

The Redis scheduler should now be managed by Supervisor. You can check its status with:

sudo supervisorctl status

13. Custom Scripts for Process Management

To simplify starting and stopping all processes, you can create custom scripts. Here are examples:

  1. Create a start script:

     nano ~/start_all.sh
    

    Add the following content:

     #!/bin/bash
     sudo supervisorctl start all
     sudo systemctl start nginx
    
  2. Create a stop script:

     nano ~/stop_all.sh
    

    Add the following content:

     #!/bin/bash
     sudo supervisorctl stop all
     sudo systemctl stop nginx
    
  3. Make these scripts executable:

     chmod +x ~/start_all.sh ~/stop_all.sh
    

Now you can start all processes with ./start_all.sh and stop them with ./stop_all.sh.

Remember to run these scripts with sudo if your user doesn't have the necessary permissions.

14. Monitoring and Maintenance

Regularly update your system:

sudo apt update
sudo apt upgrade

Monitor your logs:

tail -f /var/log/myproject.err.log
tail -f /var/log/myproject.out.log
tail -f /var/log/redis_scheduler.err.log
tail -f /var/log/redis_scheduler.out.log

15. Troubleshooting

  • If your site isn't accessible, check Nginx status: sudo systemctl status nginx

  • If Uvicorn isn't running, check Supervisor: sudo supervisorctl status

  • Check Nginx error logs: sudo tail -f /var/log/nginx/error.log

  • Ensure your firewall isn't blocking connections: sudo ufw status

16. Conclusion

You've now deployed your Django application with Uvicorn, set up Nginx as a reverse proxy, configured SSL, and set up a Redis scheduler. Your application should be accessible via HTTPS at your domain.

Remember to keep your system and applications updated, monitor your logs, and maintain good security practices.

0
Subscribe to my newsletter

Read articles from Saurav Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Saurav Sharma
Saurav Sharma

I am a Self Taught Backend developer With 3 Years of Experience. Currently, I am working at a tech Startup based in The Bahamas. Here are my skills so far - 💪Expert at - 🔹Python 🔹Django 🔹Django REST framework 🔹Celery ( for distributed tasks ) 🔹ORM ( Know how to write fast queries & design models ) 🔹Django 3rd party packages along with postgresQL and mysql as Databases. 🔹Cache using Redis & Memcache 🔹Numpy + OpenCV for Image Processing 🔹ElasticSearch + HayStack 🔹Linux ( Debian ) 😎 Working Knowledge - Html, CSS, JavaScript, Ajax, Jquery, Git ( GitHub & BitBucket ), Basic React & React Native, Linux ( Arch ), MongoDB, VPS 🤠 Currently Learning - 🔹More Deep Dive into Django 🔹Docker 🔹Making APIs more Robust 🔹NeoVim 🔹System Design ☺️ Will Be Learn in upcoming months - 🔹GraphQL 🔹 Rust language Other than above, there is not a single technology ever exists that i can't master if needed.