DEV Community

Hein Khant Zaw
Hein Khant Zaw

Posted on

4 1

Django Deployment with Nginx in Ubuntu

This was just a note for myself as I faced several errors during my first deployment with Django. This note is for those who are facing problems during the deployment. I hope it helps!

Install the Packages from the Ubuntu Repositories first. If you are using Django with Python 3, type:

sudo apt-get update
sudo apt-get install python3-pip python3-dev libpq-dev nginx
Enter fullscreen mode Exit fullscreen mode

Postgresql installation

sudo apt-get update
sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx 
Enter fullscreen mode Exit fullscreen mode

MySQL installation

sudo apt-get update
sudo apt-get install mysql-server
mysql_secure_installation
Enter fullscreen mode Exit fullscreen mode

Database creation

CREATE DATABASE myproject;
Enter fullscreen mode Exit fullscreen mode
  1. Postgres
CREATE USER myprojectuser WITH PASSWORD 'password'; 
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;
Enter fullscreen mode Exit fullscreen mode
  1. MySQL
CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'password'; 
GRANT ALL PRIVILEGES ON * . * TO 'newuser'@'localhost';
Enter fullscreen mode Exit fullscreen mode

Python VirtualEnv Creation

sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv
cd ~/myproject
virtualenv myprojectenv
source myprojectenv/bin/activate 
pip install django gunicorn psycopg2 (For Postgres)
Enter fullscreen mode Exit fullscreen mode

Changes in setting.py

ALLOWED_HOSTS = [ * ]
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': 'password',
    }
}
Enter fullscreen mode Exit fullscreen mode

Migrations

Now, we can migrate the initial database schema to our database using the management script:

python manage.py makemigrations
python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Create an administrative user for the project by typing:

python manage.py createsuperuser
Enter fullscreen mode Exit fullscreen mode

You will have to select a username, provide an email address, and choose and confirm a password.
We can collect all of the static content into the directory location we configured by typing:

python manage.py collectstatic
Enter fullscreen mode Exit fullscreen mode

Create a Gunicorn systemd Service File

Now, we are going to implement a more robust way of starting and stopping the application server with Gunicorn. To accomplish this, we’ll make a systemd service file.

Create and open a systemd service file for Gunicorn with sudo privileges in your text editor:

sudo nano /etc/systemd/system/gunicorn.service
Enter fullscreen mode Exit fullscreen mode

gunicorn.service file

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

[Service]
Type=notify
# the specific user that our service will run as
User=root
Group=root
# another option for an even more restricted service is
# DynamicUser=yes
# see http://0pointer.net/blog/dynamic-users-with-systemd.html
RuntimeDirectory=gunicorn
WorkingDirectory=/var/www/html/[ProjectName]
ExecStart=/var/www/html/[ProjectName]/project_venv/bin/gunicorn config.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

Check for the Gunicorn Socket File

Check the status of the process to find out whether it was able to start:

sudo systemctl status gunicorn
Enter fullscreen mode Exit fullscreen mode

Create gunicorn.socket

sudo nano /etc/systemd/system/gunicorn.socket
Enter fullscreen mode Exit fullscreen mode

gunicorn.socket File

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock
# Our service won't need permissions for the socket, since it
# inherits the file descriptor by socket activation
# only the nginx daemon will need access to the socket
User=www-data
# Optionally restrict the socket permissions even more.
# Mode=600

[Install]
WantedBy=sockets.target
Enter fullscreen mode Exit fullscreen mode

Run gunicorn service

sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo systemctl status gunicorn
sudo systemctl daemon-reload
sudo systemctl restart gunicorn
Enter fullscreen mode Exit fullscreen mode

Configure Nginx to Proxy Pass to Gunicorn

sudo nano /etc/nginx/sites-available/[ProjectName]
Enter fullscreen mode Exit fullscreen mode

Then, type

upstream app_server {
    server unix:/run/gunicorn.sock fail_timeout=0;
}

server {
    listen 80;
    listen [::]:80;
    server_name 128.199.221.253;  # here can also be the IP address of the server

    keepalive_timeout 5;
    client_max_body_size 4G;

    access_log /var/www/html/[ProjectName]/logs/nginx-access.log;
    error_log /var/www/html/[ProjectName]/logs/nginx-error.log;

    location /static/ {
        autoindex on;
        alias /var/www/html/[ProjectName]/static/;
    }

    # checks for static file, if not found proxy to app
    location / {
        try_files $uri @proxy_to_app;
      }

    location @proxy_to_app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://app_server;
    }
  }
Enter fullscreen mode Exit fullscreen mode

Adding SSL Certificate

upstream app_server {
    server unix:/run/gunicorn.sock fail_timeout=0;
}

server {
    listen 80;
    listen [::]:80;
    server_name 128.199.79.246;  # here can also be the IP address of the server
    return 301 https://[DomainName]$request_uri;
  }

server{
    # SSL configuration
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name [DomainName];

    ssl        on;
    ssl_certificate         /etc/ssl/certs/cert.pem;
    ssl_certificate_key     /etc/ssl/private/key.pem;
    ssl_client_certificate /etc/ssl/certs/cloudflare.crt;
    ssl_verify_client on;

    keepalive_timeout 5;
    client_max_body_size 4G;

    access_log /home/[UserName]/logs/nginx-access.log;
    error_log /home/[UserName]/logs/nginx-error.log;

    location /static/ {
        autoindex on;
        alias /home/[UserName]/[ProjectName]/[DjangoAppName]/static/;
    }

    # checks for static file, if not found proxy to app
    location / {
        try_files $uri @proxy_to_app;
      }

    location @proxy_to_app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://app_server;
    }
}
Enter fullscreen mode Exit fullscreen mode

Restart all the service

sudo systemctl daemon-reload
sudo systemctl restart gunicorn
sudo nginx -t && sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more