How to deploy a Django app on an AWS EC2 instance
Tags: django, aws, namecheap, nginx
Published: 2023-04-21 09:47:04

Today I'll be writing on how to deploy a Django app on an AWS EC2 instance. Ofcourse there are many ways to deploy a Django app to AWS so therefore we will be looking at one of the ways to do that.

The first thing you need to do is to log in to your AWS account and search for EC2. Click on it and it would take you to your EC2 dashboard.

## Create security group
Next thing you need to do is to create a security group. 
A security group is a virtual firewall that controls the inbound and outbound network traffic for one or more instances. It acts as a filter for the traffic that can reach the instances and specifies the types of traffic, such as HTTP, HTTPS, SSH, etc., that are allowed to access the instances.
To create a security group, on your EC2 dashboard under the resources section, click on security group

At the top right corner, click the "create security group" button and then give your security group a name and description. Scroll down and add your inbound rules as shown in the picture

![image of security groups to add to EC2 instance](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2ppkin6w4hxsqrc1zzw.png)
Once you've added that you can scroll down and create the security group. Do well to keep in mind the name of your just created security group we'll be needing it shortly

## Create EC2 Instance
Now we have gotten that out of the way, it's now time to create our first EC2 instance. Head back to your EC2 dashboard and click on _instances (running)_ and then click on _launch instance_ at the top right corner. Give your instance a name and then select Ubuntu as your Application and OS Images (Amazon Machine Image). Make sure it's the free tier eligible one you selected. scroll down and double check that your instance type (either t2 or t3) is also a free tier eligible one. For the sake of simplicity of this tutorial, well leave out creating a key pair so just ignore that section and scroll down.
Now under **Network settings**, click on _Select existing security group_ and then select the security group you created earlier from the first section of this post (hope you still remember the name?) from the drop down. Leave every other setting in default and click on _launch instance_ at the bottom right. If you did every thing right, your instance should be up and running in a few seconds.

## Connect to your instance
Back on your EC2 dashboard, click on _instances_ and you should see your just created instance, ensure that the status of your instance is _running_ before you proceed to the next steps. Now to connect to your instance, select the instance you want to connect to and the click on _connect_. Click on the connect button again and you should be redirected to a new tab that has connected to your instance.

## Now the main work 😅
Now that we have connected to our EC2 instance, we can now start the dirty work. First we have to install some packages

```
sudo apt update
sudo apt install python3-venv python3-dev libpq-dev postgresql postgresql-contrib nginx curl
```
This command will install a necessary packages you need to create virtual environments for your Python projects, build Gunicorn, create a Postgres database system and the libraries needed to interact with it, and the Nginx web server.

## Create the PostgreSQL Database and User
Next we have to create a PostgreSQL database for our django application. If you are fine using the default sqlite database, feel free to skip this section

Log into an interactive Postgres session by typing:

```
sudo -u postgres psql
```
Next, create a database for your project:

```
CREATE DATABASE djangodb;
```
Next, you have to create a database user for your project.

```
CREATE USER djangouser WITH PASSWORD 'put_a_secure_password';
```
Django needs the following parameters for its database connections so let's add them

```
ALTER ROLE djangouser SET client_encoding TO 'utf8';
ALTER ROLE djangouser SET default_transaction_isolation TO 'read committed';
ALTER ROLE djangouser SET timezone TO 'UTC';
```
Next, give the new user access to control the new database.

```
GRANT ALL PRIVILEGES ON DATABASE djangodb TO djangouser;
```

## Create a Python Virtual Environment for your Project
Up next to create a virtual environment and install the Python requirements within the virtual environment for easier management.

```
python3 -m venv venv
```
Next you activate the virtual environment

```
source venv/bin/activate
```
## Setting up Your django project.

After that the virtual environment has been activated, the next thing is to clone the github repository you want to deploy. You can do that using the following commands below

```
git clone https://github.com/sampleusername/myproj.git
```
Next is to install some necessary packages in other to run a basic django app. 
```
pip install django gunicorn psycopg2-binary
```
You can also install other libraries and packages in your requirements.txt file

```
cd myproj/
pip install -r requirements.txt
```

## Configure the Project Settings
You should make sure that you have setup your database variables in your `settings.py` for the database connection to the postgres database you created earlier. And example configuration is shown below. Open the settings file in a text editor. You can do that using nano

```
nano myproj/myproj/settings.py
```
Then you look for the database connection and make the necessary changes using your own details

```
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'djangodb',
        'USER': 'djangouser',
        'PASSWORD': 'put_a_secure_password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

```
You should also set your allowed hosts

```
ALLOWED_HOSTS = ['Your_Public_IPv4 address', 'domain_name', . . ., 'localhost']
```
If you are using a custom domain name, after you have added the domain name to your allowed host, you would also need to add a host record. You can use the sample picture below to guide you on how to set it up.


![picture of adding your host records to namecheap](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/34jsqtvw2stf0k2ye6tp.png)

> You can also use also decide to use Route 53 and set up an elastic IP address if you wish. It is actually the recommended way to setup your custom domain name. you can check out this [tutorial](https://techgenix.com/namecheap-aws-ec2-linux/) on how to do that.

## Migrate your django migrations
Now, you can migrate the initial database schema to our PostgreSQL database using the management script:

```
python manage.py makemigrations
python manage.py migrate
python manage.py collectstatic
```

## Set up Supervisor
The next thing we would want to do is to install supervisor. Now supervisor is there to ensure that our application keeps on running smoothly in the background.
First let's install it

```
sudo apt-get install supervisor
```
Then we need to configure a Gunicorn process to ensure that our server (Gunicorn) starts automatically with the system and that it can automatically restart if for some reason it exits unexpectedly. To do that, we need to change directory to where the configuration files should be placed. You can do that by 
```
cd /etc/supervisor/conf.d 
```
Then create a `guincorn.conf` file 

```
sudo nano gunicorn.conf
```
paste the following code
```
[program:gunicorn]
directory=/home/ubuntu/myproj
command=/home/ubuntu/venv/bin/gunicorn --workers 3 --bind unix:/home/ubuntu/myproj/app.sock myproj.wsgi:application 
autostart=true
autorestart=true
stderr_logfile=/var/log/gunicorn/gunicorn.err.log
stdout_logfile=/var/log/gunicorn/gunicorn.out.log
```

> Make sure to change all instances of myproj to your actuall project name

save and close the file once you're done. You can do so by pressing `ctrl o` then press enter. To close the file press `ctrl x`

Once we have done that, we'll need to create the log file directory. 

```
sudo mkdir /var/log/gunicorn
```
Finally we then need to notify the supervisor of the changes we just made using `supervisorctl`

```
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start all
sudo supervisorctl status all
```
> Make sure that when you check for your status it shows `running`. It it's not, you'll need to debug your code for errors. You can check for errors in your log file in `/var/log/gunicorn`

Below are some useful commands you can use to debug your code

```
# to check the status of our processes
sudo supervisorctl status <program_name>

# example
sudo supervisorctl status gunicorn
my_proj                          RUNNING   pid 1412, uptime 7:59:29

# to check status of all running process 
sudo supervisorctl status all


# to start, stop, restart all or some of the processes
sudo supervisorctl start <program_name|all>
sudo supervisorctl restart <program_name|all>
sudo supervisorctl stop <program_name|all>
```

## Setup Nginx
First of we need to make some changes to the nginx conf file in other to avoid permission denied errors. To that change directory to where the file is stored

```
cd /etc/nginx

#then open the conf file
sudo nano nginx.conf
```
Once the file is opened, change the `www-data` in the first line of the file to `root`. Close and save.

With that out of the way we can now go ahead to create a configuration file for our django application in the `sites-available` directory

```
sudo nano /etc/nginx/sites-available/django.conf
```
then paste the settings below

> replace appropriately with your aws public ip address and custom domain name if you have and change all instances of my proj to your actual project name
```
server{
    listen 80;
    server_name your_public_ip your_custom_domain;
    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/ubuntu/myproj;
    }
    location / {
        include proxy_params;
        proxy_pass http://unix:/home/ubuntu/myproj/app.sock;

    }
}
```
Let's do some explanations 

1. 
The first `location` tells Nginx to ignore any problems with finding a favicon. 

2. The second `location` tells where to find the static assets that you collected in your `/myproj/static` directory. All of these files have a standard URI prefix of `/static`, so you can create a location block to match those requests

3. The third finally creates a location / {} block to match all other requests. Inside of this location, includes the standard proxy_params file included with the Nginx installation which then passes the traffic directly to the Gunicorn socket

You can close the file and save it.
Next you need to test our Nginx configuration for syntax errors

```
sudo nginx -t
```
Lastly, you need to enable the file by linking it to the sites-enabled directory. To do that, 

```
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
```
And last but not the least you can go ahead to restart your nginx server

```
sudo service nginx restart
```
You can now open your website by visiting your public ip address or using your custom domain link and your site should be live

##Securing connections with HTTPS
start by installing certbot

```
sudo apt install python-certbot-nginx
```
next you need to obtain an SSL Certificate

```
sudo certbot --nginx -d myawesomedomain.com
```
You will then be prompted to provide an email, then press [ENTER]
Next you will need to accept the terms of service.
Next you will need to decide if you want to opt into some digital marketing.

Let’s Encrypt’s certificates are only valid for ninety days, this is meant to force users to automate renewal of the certificates. Luckily certbot takes care of this for us. However we still need to verify this.

```
sudo certbot renew --dry-run
```
Thank you for reading.