This guide will walk you through the steps of hosting a Django project with PostgreSQL as the database on an AWS EC2 instance, and configuring Nginx as the server to serve the Django application.
- Create an AWS account at https://aws.amazon.com/.
- Look for EC2 using the search bar.
- On the EC2 dashboard, choose the instances or simply click on "Launch Instance."
- To create a new instance, click on "Launch Instance."
- In the "Name and tags" section, enter your project name (optional).
- Choose an operating system from the Application and OS Images section,
I prefer Ubuntu
. - Select an instance type that aligns with the requirements of your project.
- Here we are configuring an EC2 instance with Ubuntu 64-bit OS and t3.micro or t2.micro type, which is eligible for AWS Free Tier choose that.
- Create and download your PEM file from the "Key pair" section, giving it your project name.
Store the PEM file in an appropriate directory and do not delete it, as it cannot be recreated.
- Verify EC2 instance network settings and permissions: Ensure "Allow SSH," "Allow HTTPS," and "Allow HTTP" are enabled. Then, click "Launch Instance."
- On your EC2 Instance Dashboard, choose the instance you've recently created.
- Click on the "Connect" button located at the top right corner.
- Select the SSH client and copy the SSH login command from the Example section (ssh -i key_.pem ec2-user@your-ec2-instance-public-ip).
-
Open Command Prompt/ Git Bash (I prefer Git Bash) in the directory where the .pem file is located.
-
Enter the command to change the .pem file permissions, setting it to read-only for the owner:
Make sure to replace "key_name.pem" with the actual name of your .pem file.
If you are using Git Bash, you can execute Linux commands. Git Bash on Windows supports Linux commands.
- For Linux: "chmod 400 key_name.pem"
- For Windows: "icacls key_name.pem /inheritance:r /grant:r %USERNAME%:R"
- Now, paste the command copied earlier from the SSH client tab.
- After pasting the command, you may encounter a prompt about host authenticity. Type "yes" and press Enter to continue connecting.
sudo apt update
sudo apt install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx curl
-
Connect to postgres :
sudo -u postgres psql
-
Create database :
CREATE DATABASE your_database_name;
-
Create user :
CREATE USER user_name WITH PASSWORD 'password';
-
Grant permissions :
-
Enter inside the created database using the command:
\c your_database_name
GRANT ALL PRIVILEGES ON DATABASE your_database_name TO user_name;
GRANT ALL PRIVILEGES ON SCHEMA PUBLIC TO username;
-
Exit postgres :
\q
sudo apt install python3-pip
sudo apt install python3.12-venv
python3 -m venv env
source env/bin/activate
pip install psycopg2-binary
Before cloning your repository, ensure you have a "requirements.txt" file containing the project dependencies. If you already have this file, you can proceed to clone the Git repository and continue with the next steps.
-
Open your project locally using VS Code or PyCharm.
-
Activate your virtual environment and navigate to the root directory of your project, where "manage.py" exists.
-
Run this command:
pip freeze > requirements.txt
-
Confirm the file creation by check your directory for the "requirements.txt" file.
git clone <your-git-repository-url>
- Navigate to the project's root directory containing the manage.py file.
- Helpful commands :
- "ls" to list all directories present in this file.
- "pwd" to display the current directory's path.
- "cd" to navigate to different directories. For example: "cd project_file_name".
- Navigate to the project's root directory using cd
- Open settings.py file in an editor : sudo nano settings.py
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "database_name",
"USER": "user_name",
"PASSWORD": "user_password",
"HOST": "localhost",
"PORT": "5432",
}
}
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
pip install -r requirements.txt
pip freeze
-
Navigate into the main directory of your project, where settings.py is located, using the "cd" command.
-
Open the
url.py
file in the main directory of project using the nano editor :sudo nano urls.py
urlpatterns = [
path("admin/", admin.site.urls),
# path("", include("user.urls")),
# path("", include("admin.urls")),
# path("accounts/", include("allauth.urls"), name="accounts"),
]
We can revert this action after completing the migrations; otherwise, it may cause errors when executing the migration commands.
python manage.py migrate
python manage.py createsuperuser
python manage.py collectstatic
-
Run the command to run the nginx server:
sudo systemctl start nginx
-
You can check if Nginx is running using this command:
sudo systemctl status nginx
-
Now, verify that the server is running by accessing the Auto-assigned IP in the instance dashboard.
-
This should display the Nginx default page.
-
Next, we need to configure Nginx to serve our website.
-
To set a domain name for your website, you'll need to purchase a domain name from a provider such as GoDaddy or Hostinger.
-
After purchasing the domain, you'll need to connect it with your instance, a process known as Domain Name Resolution.
-
In your AWS Dashboard, search for "Route 53", which is a scalable DNS and domain name registration service.
-
You can refer to the YouTube video to connect your EC2 instance with the domain you bought on GoDaddy:
-
It is necessary to install Gunicorn and configure it to run our Python website with Nginx.
pip install gunicorn
-
It is necessary to proxy pass the server requests coming to the Nginx port to the Gunicorn port.
-
We need to configure a new file to proxy pass the Gunicorn port in Nginx. To do that, let's navigate to the Nginx sites-available directory.
-
Execute this command to navigate to the Nginx sites-available directory.
cd /etc/nginx/sites-available/
-
Run the command
ls
to check if the "default" file exists in the directory, which serves as the default configuration file for Nginx. -
The default file contains the configuration for displaying the Nginx default page. We should create another file for our website to maintain the standardization without altering the default configuration.
-
Now create a new file there with your project/website name instead of
website_name
sudo nano website_name
-
Executing this command will open a blank file in the nano editor.
-
Please add the following configurations, making any necessary changes where needed:
server {
listen 80;
server_name your_domain_name.com www.your_domain_name.com;
location / {
proxy_pass http://0.0.0.0:9090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
-
Now save the file using
Ctrl + O
-
Exit the file using
Ctrl + x
-
Now that we've created a new file adding the Gunicorn port to run the website, it's currently available. Next, we need to enable it.
-
There is another directory called 'sites-enabled'. We need to create the same file there as well.
-
Instead of creating it manually, We can simply create a link from 'sites-available' to 'sites-enabled' and synchronize it, which is called a
symbolic link
. -
From the 'sites-available' directory, execute this command to create the symbolic link, replacing 'file_name' with the name of the file you have created:
sudo ln -s /etc/nginx/sites-available/file_name /etc/nginx/sites-enabled/file_name
-
You can verify whether the symbolic link is created by navigating to the 'sites-enabled' directory using the following command:
cd /etc/nginx/sites-enabled/
ls
-
To verify the website runs, enter the default configuration file and comment out all lines. Otherwise, the default file will load when you access the IP address. Save and exit to continue.
-
Navigate to the /etc/nginx/sites-enabled/
sudo nano default
-
Comment out all the lines in the default configuration, save the file, and exit.
-
We've added hosts for the website to run. Therefore, in our project's settings.py file, we need to include these hosts in the 'ALLOWED_HOSTS' area.
-
Navigate to the directory where your settings.py file exists, and open it using the command:
sudo nano settings.py
. -
There will be a blank are for adding the "ALLOWED HOSTS", make the changes there:
In the 'Auto-assigned IP address' section, replace the placeholder with the IP address assigned to your instance, as displayed in your instance dashboard.
ALLOWED_HOSTS = ['Auto-assigned IP address', '0.0.0.0']
-
Save the settings file by pressing
Ctrl + O
, then pressEnter
to confirm, and exit the nano editor by pressingCtrl + X
. After that, navigate back to your root directory. -
Now restart Nginx using this command:
sudo service nginx restart
-
After that, we need to run the Gunicorn server to ensure the website is being served.
You need to change the project_name.wsgi with the proper name which will be seen in the setting file Example : WSGI_APPLICATION = "project_name.wsgi.application" , from this only take the project_name.wsgi
gunicorn --bind 0.0.0.0:9090 project_name.wsgi
-
Now, load the page using the auto-assigned IP in your browser to verify if your page is loading.
If you encounter an issue with static files not being served, follow these steps; otherwise, skip these and proceed to step-10:
-
Need to give location of the static files in the ngnix configuration
-
To do that, navigate to your root location. From there, navigate to the location where your static files are stored.
-
After navigating to the directory where your static files are stored, use the command
pwd
to display the path of the working directory. -
You should copy the path provided as the output.
-
Now, navigate to your website configuration file, which can be found in the 'sites-enabled' or 'sites-available' directory, and use the
sudo nano
command to open the file using the editor. -
In the server block of your configuration, there should be a location block where you've set up the proxy_pass. We also need to add another location block for serving static files inside the server block.
-
You can use the following code snippet, making the change of pasting the static file location that you copied earlier using the
pwd
command:location /static/ { autoindex on; alias /home/path/to/your/static/files/; }
Don't forget to include the last '/' in the location of static, otherwise it will not work.
-
Save the configuration file by pressing
Ctrl + O
, then pressEnter
to confirm, and exit the nano editor by pressingCtrl + X
. -
Use this command:
ps aux | grep nginx
The command ps aux | grep nginx is used to list all running processes (ps aux) and filter out only those lines that contain the string "nginx" (grep nginx).
- To grant permissions for accessing certain files or directories in web server configurations, we need to add the user
www-data
to theubuntu
group. The command to achieve this is:
sudo usermod -a -G ubuntu www-data
This allows the web server, typically running under the www-data
user, to access specific files or directories required for serving web content.
After adding the user www-data
to the ubuntu
group, it's common practice to navigate into the root directory of your project and use the command:
You may need to adjust the 'static' file name in the command according to what you have named it.
sudo chown -R :www-data static
This command changes the ownership of the static
directory and all its contents to the www-data
group. This ensures that the web server, which typically runs under the www-data
user or group, has the necessary permissions to read and write to the static files within your project.
-
Now restart the Nginx server:
sudo service nginx restart
-
Run the Gunicorn server:
gunicorn --bind 0.0.0.0:9090 project_name.wsgi
-
Run the IP in browser to check whether the page is running.
-
Install Certbot and the Nginx plugin:
sudo apt install certbot python3-certbot-nginx
-
Allowing HTTPS Through the Firewall
We need to allow HTTPS traffic, so let's check if Nginx has registered it.
Check the firewall status:
sudo ufw status
If the status is inactive, you can skip this step. However, if it is active, run the following commands to enable HTTPS traffic. It is advisable to block all other ports except the ones you want to allow traffic through.
Enable HTTPS traffic:
sudo ufw allow 'Nginx Full'
sudo ufw delete allow 'Nginx HTTP'
-
Obtain an SSL Certificate
To acquire an SSL certificate, execute the following command and follow the prompts:
Replace your_domain.com with the domain name you purchased from GoDaddy
sudo certbot --nginx -d your_domain.com -d www.your_domain.com
-
Now a prompt will come like this:
Obtaining a new certificate Performing the following challenges: http-01 challenge for prodproject.net Waiting for verification... Cleaning up challenges Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/prod_project.conf 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):
You should select option 2 here to redirect all the HTTP requests to HTTPS.
-
This will acquire an SSL certificate for your domain and configure Nginx to use it.
-
Update Nginx Configuration
After obtaining the SSL certificate, update your Nginx configuration file to use it. Open the configuration file in the text editor:
sudo nano /etc/nginx/sites-available/your_site
-
If the following lines are not automatically added, locate the server block in your configuration file and insert them within the server block:
listen [::]:443 ssl ipv6only=on; listen 443 ssl; ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
Save the file and exit the text editor.
-
Test and Reload Nginx
Before you restart Nginx, you should test the configuration to ensure there are no syntax errors:
sudo nginx -t
-
Verifying Certbot Auto-Renewal
Let’s Encrypt certificates expire every ninety days, prompting users to automate renewal. Certbot manages this process, but we still need to verify its functionality.
sudo certbot renew --dry-run
-
Navigate to the "settings.py" file and add the domain name's to the ALLOWED_HOSTS section:
ALLOWED_HOSTS = ['Auto-assigned IP address', '0.0.0.0', 'your_domain_name.com', 'www.your_domain_name.com']
-
In the settings.py file, you may also need to set DEBUG = False so that standard error pages will be shown instead of developer errors:
DEBUG = False
-
Now you can check the page by running the server:
sudo service nginx restart
gunicorn --bind 0.0.0.0:9090 project_name.wsgi
-
Verify if the page has switched from HTTP to HTTPS.
Simply close the command prompt or Git Bash; the server will continue running.