Mr. Editor-in-chief Mr. Editor-in-chief May 15, 2022 Updated April 24, 2026

Deploy Multiple Django projects with Gunicorn and Nginx

AWS EC2 set up

  • Go to AWS Console Home -> Search 'ec2' -> EC2 (Virtual Servers in the Cloud) -> Security Groups -> Create security group -> Inbound rules -> Outbound rules (this is for git-cloning codes from GitHub later)

  • Make sure the security group inbound rules and outbound rules consistent with future firewall settings. (In fact we don't need to enable ufw inside EC2 machine, AWS provides firewall/Security groups which are already sufficient.)

  • Back to EC2 Dashboard -> Launch instance -> Key pair name -> Select a key pair (Create one if no key pairs available yet) -> Firewall(security groups) -> Select the security group just created -> Launch instance

SSH into EC2 VM

ssh ubuntu@52.199.20.253

Add a new user and delete default user(ubuntu) (Optional)

sudo adduser user0
password: happypass
sudo adduser user0 sudo
cd /home/user0 # if not working: su user0
sudo mkdir .ssh
cd .ssh
sudo touch authorized_keys
cat /home/ubuntu/.ssh/authorized_keys
# copy the content
sudo nano authorized_keys
# paste here
ctrl + x,  y,  enter
# 'sudo cp /home/ubuntu/.ssh/authorized_keys .ssh/authorized_keys' DOES NOT WORK (reason unknown)
exit
ssh user0@52.199.20.253
sudo visudo
# Add user0 ALL = NOPASSWD : ALL
sudo deluser --remove-home ubuntu

Deploy Preparation

sudo apt-get update && sudo apt-get upgrade  

# Hostname change (Optional)
hostname
sudo hostnamectl set-hostname [hostname]
sudo nano /etc/hosts

ssh-keygen -t ecdsa -b 521
cat ~/.ssh/id_ecdsa.pub
# Copy the content and Add it to your GitHub SSH and GPG keys
git clone git@github.com:username/repo_name.git

sudo apt-get install -y nginx

Serving multiple Django apps with Gunicorn and Nginx
YouTube Reference
Suppose we have two django projects located under /home/user0 directory, we have created & activated a virtual environment and pip installed gunicorn for each inside the project folder.

sudo nano /etc/systemd/system/django_one.socket
[Unit]
 Description=django_one socket
[Socket]
 ListenStream=/run/django_one.sock
[Install]
 WantedBy=sockets.target
sudo nano /etc/systemd/system/django_one.service
[Unit]
Description=django_one daemon
Requires=django_one.socket
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/home/user0/django_one
ExecStart=/home/user0/django_one/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/django_one.sock \
          django_one.wsgi:application
[Install]
WantedBy=multi-user.target
sudo systemctl start django_one.socket
sudo systemctl start django_one.service
sudo systemctl enable django_one.socket
sudo systemctl enable django_one.service
sudo systemctl status django_one

We are going to do the same with django_two project

sudo nano /etc/systemd/system/django_two.socket
[Unit]
 Description=django_two socket
[Socket]
 ListenStream=/run/django_two.sock
[Install]
 WantedBy=sockets.target
sudo nano /etc/systemd/system/django_two.service
[Unit]
Description=django_two daemon
Requires=django_two.socket
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/home/user0/django_two
ExecStart=/home/user0/django_two/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/django_two.sock \
          django_two.wsgi:application
[Install]
WantedBy=multi-user.target
sudo systemctl start django_two.socket
sudo systemctl start django_two.service
sudo systemctl enable django_two.socket
sudo systemctl enable django_two.service
sudo systemctl status django_two

Nginx set up

sudo nano /etc/nginx/nginx.conf
user root;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
    worker_connections 1024;
}
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile            on;
        tcp_nopush          on;
        tcp_nodelay         on;
        keepalive_timeout   65;
        types_hash_max_size 2048;
    include             /etc/nginx/mime.types;
        default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include for more information.

    include /etc/nginx/conf.d/*.conf;

    server {
        listen 80;
        server_name blog.peng.codes;

        location = /favicon.ico { 
            access_log off; 
            log_not_found off; 
            }
        location /static {
            root /home/user0/django_one;  
        }

        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_pass http://unix:/run/django_one.sock;
            }
        }
    server {
        listen 80;
        server_name test.peng.codes;
        location = /favicon.ico { 
            access_log off; 
            log_not_found off; 
        }
        location /static {
                root /home/user0/django_two;

                    }
        location / {
                    #include proxy_params;
                    proxy_set_header Host $http_host;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    proxy_pass http://unix:/run/django_two.sock;
                }
        }

    server {
            listen       80 default_server;
            listen       [::]:80 default_server;
            server_name  _;
            root         /usr/share/nginx/html;

            # Load configuration files for the default server block.
            include /etc/nginx/default.d/*.conf;
            location / {
                    }
            error_page 404 /404.html;
            location = /40x.html {
                    }
            error_page 500 502 503 504 /50x.html;
            location = /50x.html {
                    }
        }

    # Settings for a TLS enabled server.
    # server {
    #         listen       443 ssl http2 default_server;
    #         listen       [::]:443 ssl http2 default_server;
    #         server_name  _;
    #         root         /usr/share/nginx/html;

    #         ssl_certificate "/etc/pki/nginx/server.crt";
    #         ssl_certificate_key "/etc/pki/nginx/private/server.key";
    #         ssl_session_cache shared:SSL:1m;
    #         ssl_session_timeout  10m;
    #         ssl_ciphers HIGH:!aNULL:!MD5;
    #         ssl_prefer_server_ciphers on;

    #         # Load configuration files for the default server block.
    #         include /etc/nginx/default.d/*.conf;

    #         location / {
    #         }

    #         error_page 404 /404.html;
    #             location = /40x.html {
    #         }

    #         error_page 500 502 503 504 /50x.html;
    #             location = /50x.html {
    #         }
    #     }
}
systemctl restart nginx
systemctl status nginx.service