Deploying Django on AWS ec2 ubuntu Instance with Uwsgi and Nginx

You need a Linux based system, I use Ubuntu 18.04 for this tutorial

Rakesh Samal
4 min readSep 21, 2020

Setup your Django App

Step 1: Access your AWS Ubuntu instance on terminal

ssh ubuntu@your-aws-instance-public-ip -i key.pem

Step 2: After logging in, let’s get all the required stuff installed.

sudo apt-get update
sudo apt-get install python-dev
sudo apt-get install python-pip

For Python 3,

sudo apt-get install python3-dev
sudo apt-get install python3-pip

Step 3: CLone of your Django project repo.

git clone <project_link>

Step 4: Install a virtualenv package

sudo pip install virtualenv

Step 5: Let’s start the virtualenv and enter it.

virtualenv venv
source venv/bin/activate

Note: If you are having problems because of python version conflict (which you will if you are using python 3 like me) then use:-

virtualenv -p python3 venv

Step 6: Go to your project path & Install all the requirements

cd your_project
pip install -r requirements.txt

Step 7: Install all the requirements

pip install -r requirements.txt

Step 8. To test if this installation worked, we need to do a runserver on port 8000 (make that this port is open to the public: you can do this by adding an inbound rule for port 8000 on the security group for your EC2 Instance) and open it in the browser.

python manage.py runserver 0.0.0.0:8000

Visit your domain and verify your running project

http://<public IP>:8000

Setup uWSGI

Now we’ve got a Django app project that runs inside the virtualenv. We need Uwsgi to serve Django to the web instead of runserver command.

deactivate

Step 1: Now install uWSGI

sudo pip install uwsgi

Step 2: Try running your project server with uWSGI

uwsgi --http :8000 --home /home/ubuntu/venv/ --chdir /home/ubuntu/<your_project> --module <your_project_name>.wsgi

Now if you fire up http://<public IP>:8000, your Django website should show up in the browser.

Step 3: Create uWSGI config ini file.

sudo mkdir -p /etc/uwsgi/sites
sudo nano /etc/uwsgi/sites/<your_project_name>.ini

project_name.ini

[uwsgi]
project = <project_name>
uid = ubuntu
base = /home/ubuntu
chdir = /home/ubuntu/<Project_name>
home = /home/ubuntu/venv
module = <Project_name>.wsgi:application
#master = true
processes = 25
harakiri = 300
http-timeout = 240
socket-timeout = 240
worker-reload-mercy = 240
reload-mercy = 240
mule-reload-mercy = 240
socket = /run/uwsgi/<Project_name>.sock
chown-socket = ubuntu:ubuntu
chmod-socket = 664
logto = /home/ubuntu/<Project_name>/uwsgi.log

You can test if this works by running the following command.

uwsgi --ini /etc/uwsgi/sites/<Project_name>.ini

Step 4: Now create a systemd Unit File for uWSGI. This will help uWSGI to automatically reboot whenever nginx or our instance is rebooted/restarted as well.

sudo nano /etc/systemd/system/uwsgi.service

uwsgi.service

[Unit]
Description=uWSGI Emperor service
[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown ubuntu:ubuntu /run/uwsgi'
ExecStart=/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
Restart=always
KillSignal=SIGQUIT
Type=notify
NotifyAccess=all
[Install]
WantedBy=multi-user.target

The service will execute this line everytime it comes up and make sure it is up. You could even fire it up on the terminal to see that it runs the server for you. The only special thing here is the –emperor. The emperor mode checks a particular folder (in our case, sites) for .ini files and fires each of them (our <project_name>.ini is sitting there) making it useful if we have multiple websites.

/usr/local/bin/uwsgi --emperor /etc/uwsgi/sites

Now let’s systemd to run our service.

sudo systemctl restart uwsgi

Setup Nginx

Step 1: Install Nginx

sudo apt-get install nginx

Step 2: Create Nginx config file

sudo nano /etc/nginx/sites-available/<project_name>

my_project

server {
listen 80;
return 301 https://$host$request_uri;
}
server{
listen 443 ssl ;
server_name <DOMAIN NAME>;
# add Strict-Transport-Security to prevent man in the middle attacks
add_header Strict-Transport-Security "max-age=31536000" always;
#location = /favicon.ico {access_log off; log_not_found off; }
ssl_certificate <.pem file path>;
ssl_certificate_key <.key file path>;
location / {
include uwsgi_params;
uwsgi_pass unix:/run/uwsgi/<project_name>.sock;
uwsgi_read_timeout 600;
# when a client closes the connection then keep the channel to uwsgi open. Otherwise uwsgi throws an IOError
uwsgi_ignore_client_abort on;
}
}

step 3: Link this config file to Nginx’s sites-enabled directory to enable them.

sudo ln -s /etc/nginx/sites-available/<project_name> /etc/nginx/sites-enabled/<project_name>

Step 4: Verify your nginx config syntax

sudo nginx -t

Step 5: After that restart Nginx service to load the new config

sudo systemctl restart nginx

Step 6: enable both of the services to start automatically at boot

sudo systemctl enable nginx
sudo systemctl enable uwsgi

Step 7: If you are not able to see your application on your domain and getting “nginx bad gateway 502”, then check your error logs

uWSGI error logs

sudo journalctl -u uwsgi

Nginx error logs

sudo tail -f /var/log/nginx/error.log

You could run into 400 or 502 errors when trying to serve if you’re running with DEBUG = False and have not set ALLOWED_HOSTS in settings.

You need to have allowed hosts configured to allow those domains. You could allow everything,

ALLOWED_HOST = ['*'] #INCASE you want allow every host but this may turn out to be unsafe

Or allow the domains we configured in the nginx conf,

ALLOWED_HOST = ['yourdomain.com','www.yourdomain.com','your-ec2-public-ip']

Step 8. If you modify any of the config files do not forget to type in

sudo systemctl daemon-reload

And finally, we’re live with our django website.

--

--