How to deploy Django application on AWS Ubuntu EC2

How to deploy Django application on AWS Ubuntu EC2 with Nginx and Uwsgi — A Practical Guide

Gautamankul
AWS Tip

--

I have done many Django-related blogs but today we gonna host the django application AWS EC2 Instance.

Prerequisites and Steps:

  1. A Free Tier AWS Account
  2. Create an EC2 Instance
  3. Login To Instance
  4. Config ubuntu server
  5. Set up read and write permission for files
  6. Complete the initial Setup project
  7. Create default for virtual host

To start with AWS, you can create an AWS account to enjoy some free tier products that include the server on that we will deploy the Django project on. AWS likes to call these servers EC2 instances.

1. Create an EC2 Instance :

After Login AWS Free Tier(root), we gonna create EC2 Instance which is similar to a private ubuntu server/system. On the console click >> Services.

How to deploy Django application on AWS Ubuntu EC2

Afterward, click compute.

then, click EC2.

After clicking on instance then a new window will open then click Launch Instances.

Then write your server name in my case demo server .

And afterward, choose your private server in my case I am using an Ubuntu server. And one more thing please choose free tier eligible an instance of your server.

And Leave the Instance type default and move to create key Pair for login your private server.

And follow below.

And Leave the rest of all settings. Go to all running instances and click instance Id(ex:i-01c055ff41496e13e).

then you got the following screen, And when we click open address then it will show This site can’t be reached.

For That, we need to allow security for HTTPS and TCP in the Security Group.

After clicking, Security Groups then click Edit Inbound rules.

Here, what is we are doing?

We are allowing coming traffic particular IP Address therefore we can access it anywhere.

Here, we allow HTTP, HTTPS, and All TCP requests from anywhere. Click save changes.

2. Login To Instance :

Personally, I will use MobaXstrem for using a private cloud server.

Click >> sessions >> SSH >> follow the below image.

Here, in place of the Remote Host use EC2 Instance public Ip Address 123.34.567.36 and In place of username for default username is ubuntu for Ubuntu Server and we gonna provide the full path of our key-pair .pem file.

3. Config ubuntu server:

Now, time to configure your private ubuntu server first we gonna update and upgrade the server

$ sudo apt-get update
$ sudo apt-get upgrade

afterward, we gonna install python3 as well as apache2 .

$ sudo apt install python3-venv libpq-dev nginx curl

Then install virtualenv python package for the virtual environment of projects.

$ sudo pip3 -H install virtualenv

4. Set up read and write permission for files:

In my Project, I’m using Python Decouple for strictly some information then I have to create .env files with write and read permission.

$ whoami
$ groups

By default, your’s ubuntu username and group name are the same which is ubuntu

$ sudo chown ubuntu:ubuntu /home/ubuntu/DemoProject/.env

in my .env configuration

SECRET_KEY=3izb^ryglj(bvrjb2_y1fZvcnbky#358_l6-nn#i8fkug4mmz!
DEBUG=True
DB_NAME=HELLO_DJANGO
DB_USER=U_HELLO
DB_PASSWORD=hA8(scA@!fg3*sc&xaGh&6%-l<._&xCf
DB_HOST=127.0.0.1

If you are working with Git, update your .gitignore adding the .env file so you don’t commit any sensitive data to your remote repository.

Now import the library:

from decouple import config

Retrieve the settings parameters:

import os
from decouple import config

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', cast=bool)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': config('DB_NAME'),
'USER': config('DB_USER'),
'PASSWORD': config('DB_PASSWORD'),
'HOST': config('DB_HOST'),
'PORT': '',
}
}

# Email settings
EMAIL_USE_TLS = config('EMAIL_USE_TLS', cast=bool)
EMAIL_HOST = config('EMAIL_HOST')
EMAIL_PORT = config('EMAIL_PORT', cast=int)
EMAIL_HOST_USER = config('EMAIL_HOST_USER')

Done…

5. Complete Initial Setup Project:

I have already a project so I gonna clone it from my GitHub page. If you have a project then clone it or if you want to create a new project then you can.

I am cloning my project on this path /home/ubuntu . Because your default user is ubuntu .

and in the root directory of the django project create a virtual environment.

$ virtualenv venv

Now change some configurations in settings.py .

ALLOWED_HOSTS = ["server_domain_or_IP"]
# Static files settings
STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "static")
# Media files settings
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Now activate the virtual environment.

$ source venv/bin/activate
(venv) $ pip3 install -r requriments.txt
(venv) $ python3 manage.py makemigrations
(venv) $ python3 manage.py migrate
(venv) $ python3 manage.py creatuperuser

We can collect all of the static content into the directory location we configured by typing

(venv)$ python3 manage.py collectstatic

Allow connections to the development server by typing:

(venv)$ sudo ufw allow 8000

Finally, you can test your project by starting up the Django development server with this command:

(venv)$ python3 manage.py runserver 0.0.0.0:8000

In your web browser, visit your server’s domain name or IP address followed by :8000:

http://server_domain_or_IP:8000

You should see the default Django index page:

Note: Now we gonna test on the local server by using gunicorn.

(venv)$ pip3 install gunicorn

and then

(venv)$ gunicorn --bind 0.0.0.0:8000 myproject.wsgi

6. Creating systemd Socket and Service Files for Gunicorn:

Let’s create a system socket file for gunicorn now. Inside, you’ll create a [Unit] section to describe the socket, a [Socket] section to define the socket location, and an [Install] section to make sure the socket is created at the right time:

$ sudo nano /etc/systemd/system/gunicorn.socket

Paste the contents below and save the file

[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

next, we gonna create a service file who able to serve your project.

$ sudo nano /etc/systemd/system/gunicorn.service

Paste the contents below inside this file.

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

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/bhoomi
ExecStart=/home/ubuntu/bhoomi/venv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
BhoomiProject.wsgi:application

[Install]
WantedBy=multi-user.target

If it says file permission denied so use.

$ sudo chown ubuntu:ubuntu /etc/systemd/system/gunicorn.socket
$ sudo chown ubuntu:ubuntu /etc/systemd/system/gunicorn.service

If the systemctl status command indicated an error occurred or if you do not find the gunicorn.sock file in the directory, this indicates that the Gunicorn socket was not created correctly. Check the Gunicorn socket’s logs by typing

$ sudo journalctl -u gunicorn.socket

Let's now start and enable the gunicorn socket.

$ sudo systemctl start gunicorn.socket
$ sudo systemctl enable gunicorn.socket

Let's check the gunicorn socket status.

$ sudo systemctl status gunicorn.socket
$ sudo systemctl daemon-reload

Then we gonna use to curl to fetch the index.html in the django application.

$ curl --unix-socket /run/gunicorn.sock localhost

Check again status of gunicorn

$ sudo systemctl status gunicorn

7. Configure Nginx to Proxy Pass to Gunicorn:

Now that Gunicorn is set up, you’ll need to configure Nginx to pass traffic to the process.

Start by creating and opening a new server block in Nginx’s sites-available directory:

$ sudo nano /etc/nginx/sites-available/demo

Paste the below contents inside the file-created

server {
listen 80;
server_name 206.189.142.76;

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

location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}

the location used, to create a location / {} block to match all other requests. Inside this location, you’ll include the standard proxy_params file included with the Nginx installation and then you’ll pass the traffic directly to the Gunicorn socket.

Update the Nginx config file at /etc/nginx/nginx.conf so we can upload large files (images)

http{
##
# Basic Settings
##
client_max_body_size 10M;
}

NOTE: Other Wise you may face 413 Request Entity Too Large.

Activate the configuration using the following command. before that disable the default nginx server configuration.

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

and then,

$ sudo ln -s /etc/nginx/sites-available/demo /etc/nginx/sites-enabled/

Restart nginx and allow the changes to take place

$ sudo systemctl restart nginx

Test your Nginx configuration for syntax errors by typing

$ sudo nginx -t

Finally, you’ll need to open the firewall to normal traffic on port 80. Since you no longer need access to the development server, you can remove the rule to open port 8000 as well.

$ sudo ufw delete allow 8000
$ sudo ufw allow 'Nginx Full'

9. Troubleshooting Nginx and Gunicorn:

  1. Nginx Is Displaying a 502 Bad Gateway.
$ sudo tail -F /var/log/nginx/error.log  

2. Permission to read and write mod.

$ sudo chown ubuntu:ubuntu /home/ubuntu/demo/static
$ sudo chown ubuntu:ubuntu /home/ubuntu/demo/db.sqlite3
$ sudo chown ubuntu:ubuntu /home/ubuntu/demo

so why wait to serve your application with your domain

10. Point the Domain to AWS EC2:

well, we are hosted successfully now we gonna point our IP Address to a Domain or Sub-domain so let’s start.

We gonna add some security bound to our AWS Instance like for HTTPS on 443 PORT allowing Everwhere same as SSH and HTTP.

Click, On services and Go to >> Route 53

afterward, Go to DNS management and click Hosted zone.

Then click create hosted zone i my case I have already one domain.

and in the domain name write your Domain or Sub-domain name and in Type use Public Hosted Zone and click to save changes .

Then, wait to create after creation did then click create record.

Then, In leave blank in Record name, and in Record Type choose IPV4 in the section of Value use your EC2 IPv4 Address and click Add another record.

If You want to add a CNAME record you can add the following step.

and in place of the server address in the Nginx configuration file add.

server {
listen 80;
server_name 206.189.142.76 example.com;

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

location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}

and reload your Nginx server.

$ sudo systemctl reload nginx
$ sudo systemctl restart nginx
$ sudo systemctl status nginx

11. Get SSL Certificate:

So, If you want to prevent your site from an attacker then you have to get the SSL certificate

For this, I’m using Certbot from Let’s Encrypt.Install certbot for Nginx.

$ sudo apt install certbot python3-certbot-nginx

Certbot provides a variety of ways to obtain SSL certificates through plugins. The Nginx plugin will take care of reconfiguring Nginx and reloading the config whenever necessary. To use this plugin, type the following:

$ sudo certbot --nginx -d example.com

This runs certbot with the --nginx plugin, using -d to specify the domain names we’d like the certificate to be valid for.

If this is your first time running certbot, you will be prompted to enter an email address and agree to the terms of service. After doing so, certbot will communicate with the Let’s Encrypt server, then run a challenge to verify that you control the domain you’re requesting a certificate for.

If that’s successful, certbot will ask how you’d like to configure your HTTPS settings.

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Select your choice then hit ENTER. The configuration will be updated, and Nginx will reload to pick up the new settings. certbot will wrap up with a message telling you the process was successful and where your certificates are stored:

Output
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/example.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/example.com/privkey.pem
Your cert will expire on 2020-08-18. To obtain a new or tweaked
version of this certificate in the future, simply run certbot again
with the "certonly" option. To non-interactively renew *all* of
your certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

It’s done now you can serve your site.

--

--

A Software Developer Engineer & Entrepreneur and along with i am a Buddhist.