Introduction
Deploying a Django application to production can seem daunting, especially with cloud infrastructure. AWS EC2 offers a powerful and flexible environment for hosting Django apps, providing complete control over the deployment process. This guide outlines the steps to set up and deploy a Django project on an EC2 instance from scratch.
The guide covers foundational steps such as:
- Setting up a Django project.
- Configuring a secure VPC and EC2 instance.
- Using Gunicorn as the application server.
- Automating deployments with GitHub Actions and AWS CodeDeploy.
Let’s dive in and get your Django project live on AWS!
Requirement and specification
- Programming Language: Python 3.10
- Framework: Django 5.1.3
- Application Server: Gunicorn 20.1
Infrastructure Requirements
- Compute: Amazon EC2 instance within a private subnet in a VPC
- Deployment Tools: AWS CodeDeploy for automated deployments, with necessary configurations in AWS Parameter Store
CI/CD Pipeline
- GitHub Actions: Configured for continuous integration and deployment
- GitHub Secrets: Set up for secure credential management
Django basic setup
In this section, we will walk through the essential steps to get a Django project up and running, along with a simple health check API at /ping. This route will respond with a status message to verify that the application is working correctly.
Prerequisites
- Check if Python is Installed
Run python –version or python3 –version in the terminal. If no version appears, install Python as follows:
- Mac
brew install python@3.X #Replace X with the version
Code language: PHP (php)
- For Linux, Windows or Other OS: Head over to python.org and download the installation file for your OS.
- Install pip (if missing)
Use Python’s ensurepip module to install pip:
python -m ensurepip --upgrade
Step 1: Install Django and Create a Project
The following steps outline how to set up a virtual environment and install Django:
- Install virtualenv (if it’s not already installed):
pip install virtualenv
- Create and activate a virtual environment:
python -m venv venvsource venv/bin/activate # For Linux/macOS
venv\Scripts\activate #For Windows
Code language: PHP (php)
- Install Django:
pip install django
- Create a New Django Project
Next, set up a new Django project and navigate into its directory by running the following commands:
# Create a new Django project django-admin startproject myproject
# Run initial migrations to set up the database python manage.py migrate
# (Optional) Create a new app for the project python manage.py startapp healthcheck
Code language: PHP (php)
Step 2: Add ping Route
- To set up a simple health check route, open the urls.py file in your project directory (myproject/urls.py) and update it with the following code:
from django.urls import path from django.http import JsonResponse
# Simple health check function def ping(request):
return JsonResponse({"status": "ok", "message": "Service is running"}) urlpatterns = [ path('ping', ping),
# Health check route ]
Code language: PHP (php)
- Ensure the settings are ready for local testing. Open myproject/settings.py and add the following configuration if it’s not already present. Now we’ll set it to * which means allow requests from everywhere, localhost in our case.
ALLOWED_HOSTS = ['*']
Code language: JavaScript (javascript)
Step 3: Run the Django Server
Now, let’s test the application locally:
python manage.py runserver 0.0.0.0:8000
Code language: CSS (css)
To verify that the endpoint is working, visit http://localhost:8000/ping in a web browser or use curl:
curl http://localhost:8000/ping
Code language: JavaScript (javascript)
If everything is set up correctly, the expected response will be:
{
"status": "ok", "message": "Service is running"
}
Code language: JSON / JSON with Comments (json)
With the basic Django setup ready, you have a foundation for your application. Next, we’ll set up the VPC and EC2 instance to deploy it on AWS.
Cloud setup
This section covers the necessary steps to set up an EC2 instance within a private subnet inside a VPC (Virtual Private Cloud), along with an Internet-facing Application Load Balancer (ALB) to route external traffic. The ALB will act as a gateway between the public internet and your private EC2 instance, ensuring that the EC2 instance stays protected and only accessible via the ALB.
- Setting up VPC
- Navigate to the AWS console, search VPC and select create VPC.
- From the page, select VPC and more. This will create all the required subnets (both private and public), set AZs, route tables and Internet Gateway all at once.
- Enter the name and change the CIDR block if needed.
- Change any value if needed such as number of NAT gateway, number of subnet or AZs and click Create VPC, it should be created within a few minutes.
- EC2 setup
- Navigate to the AWS console, search for EC2, and click Launch Instance.
- Enter the instance name, select Ubuntu 22.04 as the OS image, and set storage to a minimum of 30 GB.
- Choose the created VPC and a private subnet, disable auto-assign public IP.
- Create a new security group to allow access only from the VPC CIDR block.
- Under Advanced settings, attach a role for SSM connection. Create a role with the AmazonSSMManagedInstanceCore policy if needed.
- Click Launch, the instance will be operational in a few minutes.
- AWS Application Load balancer setup
- Navigate to the AWS console, search Load Balancer and click on create load balancer, select Application load balancer.
- Give it a name, select scheme as internet facing, choose the IP type as per the VPC, change the VPC to the one we created, select AZs and create security group to allow traffic from all IPs to hit the load balancer.
- In the listener and routing section, create a target group using the EC2 instance we just created, set the port where the server on EC2 is running.
- Set protocol to http on port 80, default action as forward to the target group just created and click on Create load balancer, it should be up and running in a few minutes.
With the cloud environment configured, the next step is to set up Gunicorn to serve the django application.
Gunicorn
Gunicorn (Green Unicorn) is a Python WSGI HTTP server that allows your Django application to handle multiple concurrent requests efficiently. While Django’s built-in development server is useful for local testing, it’s not suitable for production. Gunicorn ensures that your app performs well under load by running multiple worker processes.
Gunicorn setup
- Install Gunicorn within your virtual environment:
pip install gunicorn
- Now, navigate to your Django project folder and run Gunicorn with:
gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application
Code language: CSS (css)
Here:
- 0.0.0.0:8000 binds Gunicorn to 0.0.0.0 address on port 8000 of the machine.
- myproject.wsgi:application points to your Django project’s WSGI application.
You can now test your app by visiting http://<your-server-ip>:8000
- Creating a Systemd Service File
To keep Gunicorn running in the background and start it automatically on system boot, we’ll create a systemd service.
- Create a new service file at /etc/systemd/system/myproject.service
- Add the following content to the file:
[Unit]
Description=Gunicorn instance to serve Django project
After=network.target
[Service]
User=user-name
Group=group-name
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/your/venv/bin/gunicorn --workers 3 --bind 0.0.0.0:8000 myproject.wsgi:application
Restart=always
[Install]
WantedBy=multi-user.target
Code language: JavaScript (javascript)
Explanation:
- User and Group: Specify the user and group to run the service.
- WorkingDirectory: The path to your Django project.
- ExecStart: The command to start Gunicorn with 3 worker processes.
- Deciding number of workers: The number of worker processes for gunicorn should generally be 2n+1 where n is the number of cores. Example: For a 2-core instance, we get 5 workers.
Running Gunicorn in the Background
Now that we have the service file, follow these steps to start and enable Gunicorn:
- Reload systemd to apply the new service file:
sudo systemctl daemon-reload
- Start the Gunicorn service:
sudo systemctl start myproject
- Enable the service to start on boot:
sudo systemctl enable myproject
- Check the status of the service
sudo systemctl status myproject
If everything is configured correctly, you should see Gunicorn running. Now, even if the server restarts, Gunicorn will launch automatically to serve your Django app.
With Gunicorn configured, your Django application is now ready to handle multiple requests efficiently in a production environment.
With Gunicorn configured, the last and final step is setting up CI/CD for seamless deployment.
CICD
In this section, we’ll implement a CI/CD (Continuous Integration and Deployment) pipeline using GitHub Actions and AWS CodeDeploy. We’ll also store sensitive environment variables securely using AWS Parameter Store and inject them into the .env file on the EC2 instance during deployment.
This setup ensures that every time you push new code to the main branch, the latest version is automatically deployed to your EC2 instance.
1. Setting up AWS CodeDeploy
- Navigate to the AWS console and search for CodeDeploy. Click Create Application.
- Enter the application name and select EC2/On-Premises as the compute platform, then click Create.
- Open the application just created and create a deployment group. Enter the basic details for the group, deployment type as in place, attach a role with AWSCodeDeployFullAccess policy.
2. Install the CodeDeploy agent:
The CodeDeploy agent needs to be installed on the Ubuntu EC2 instance. This agent is a software package that runs on the instance and interacts with CodeDeploy to deploy the application. The CodeDeploy agent can be installed by running the following script on the EC2 instance:
#!/bin/bash#install.sh
# This installs the CodeDeploy agent and its prerequisites on Ubuntu 22.04.
sudo apt-get update
sudo apt-get install ruby-full ruby-webrick wget -y
cd /tmp
wget <a href="https://aws-codedeploy-ap-southeast-1.s3.ap-southeast-1.amazonaws.com/releases/codedeploy-agent_1.3.2-1902_all.deb">https://aws-codedeploy-ap-southeast-1.s3.ap-southeast-1.amazonaws.com/releases/codedeploy-agent_1.3.2-1902_all.deb</a>
mkdir codedeploy-agent_1.3.2-1902_ubuntu22
dpkg-deb -R codedeploy-agent_1.3.2-1902_all.deb codedeploy-agent_1.3.2-1902_ubuntu22
sed 's/Depends:.*/Depends:ruby3.0/' -i ./codedeploy-agent_1.3.2-1902_ubuntu22/DEBIAN/control
dpkg-deb -b codedeploy-agent_1.3.2-1902_ubuntu22/
sudo dpkg -i codedeploy-agent_1.3.2-1902_ubuntu22.deb
systemctl list-units --type=service | grep codedeploy
sudo service codedeploy-agent status
Code language: PHP (php)
Run the script using command bash install.sh
3. Add a new parameter:
- Go to the AWS Systems Manager → Parameter Store → Create Parameter.
- Choose Standard Parameter.
- Enter a name (e.g., /myproject/DEBUG) and a value (True or other values like database credentials).
- Set the type to String or SecureString for sensitive data.
- Add other environment variables similarly (e.g., /myproject/DB_HOST, /myproject/DB_NAME).
4. Creating a GitHub Actions Workflow:
We will create a GitHub Actions workflow to automate deployment. In your project, create the following file:
/.github/workflows/main.yml
name: Deployment name
on:
workflow_dispatch:
inputs:
tags:
description: 'Deployment name'
required: <strong>true</strong>
type: string
run-name: ${{ inputs.tags }}
jobs:
deploy:
runs-on: ubuntu-latest
env:
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_KEY }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
aws-region: ${{ env.AWS_REGION }}
aws-access-key-id: ${{ env.AWS_ACCESS_KEY }}
aws-secret-access-key: ${{ env.AWS_SECRET_KEY }}
- name: Store env variables in AWS Systems Manager Parameter Store
run: aws ssm put-parameter --name "key_name" --value "${{ secrets.KEY_VALUE }}" --type "String" --overwrite
- name: Create CodeDeploy Deployment
run: |
aws deploy create-deployment \
--application-name 'application_name' \
--deployment-group-name 'deployment_group_name' \
--revision '{"revisionType":"GitHub","gitHubLocation":{"repository":"${{ github.repository }}","commitId":"${{ github.sha }}"}}' \
--output json > deployment.json
echo deployment.json
- name: Extract deployment ID
id: deployment
run: |
DEPLOYMENT_ID=$(jq -r .deploymentId deployment.json)
echo "DEPLOYMENT_ID=$DEPLOYMENT_ID" >> $GITHUB_ENV
- name: Wait for deployment to complete
id: wait_for_deployment
run: |
DEPLOYMENT_STATUS=$(aws deploy get-deployment --deployment-id ${{ env.DEPLOYMENT_ID }} --query "deploymentInfo.status" --output text)
echo "Deployment status: $DEPLOYMENT_STATUS"
while [[ "$DEPLOYMENT_STATUS" != "Succeeded" && "$DEPLOYMENT_STATUS" != "Failed" ]]; do
echo "Waiting for deployment to complete..."
sleep 30
DEPLOYMENT_STATUS=$(aws deploy get-deployment --deployment-id ${{ env.DEPLOYMENT_ID }} --query "deploymentInfo.status" --output text)
echo "Deployment status: $DEPLOYMENT_STATUS"
done
if [[ "$DEPLOYMENT_STATUS" == "Succeeded" ]]; then
echo "Deployment succeeded."
elif [[ "$DEPLOYMENT_STATUS" == "Failed" ]]; then
echo "Deployment failed."
exit 1
fi
Code language: PHP (php)
5. Creating appspec.yml for AWS Codedeploy
CodeDeploy uses a YAML file which by default is named appspec.yml and found on source code root.
Create this file with the following content.
version: 0.0
os: linux
files:
- source: /
destination: /home/ubuntu/your_project
hooks:
AfterInstall:
- location: scripts/deploy.sh
timeout: 300
runas: ubuntu
6. Deploying the new changes
The AfterInstall hook is utilized to execute a script following a successful AWS CodeDeploy deployment. We will leverage this hook to retrieve the environment variables saved during deployment and subsequently restart the server using a deploy.sh script.
#!/bin/bash# deploy.sh
KEY_VALUE=$(aws ssm get-parameter --name "key_name" --query "Parameter.Value" --output text --region "your_aws_region")
echo "" > /home/ubuntu/your_project/.envecho "KEY_NAME= $KEY_VALUE" >> /home/ubuntu/your_project/.env
sudo systemctl restart your_project.service
Code language: PHP (php)
Conclusion
Deploying a Django application on AWS with EC2, CodeDeploy, and GitHub Actions can be simplified with a clear approach. This guide covered setting up the core infrastructure: configuring a secure VPC, creating subnets, and launching an EC2 instance in a private subnet. We integrated an Application Load Balancer (ALB) for public traffic routing and used Gunicorn as the app server, automating deployments with GitHub Actions and CodeDeploy. Sensitive data is managed securely with AWS Parameter Store, ensuring a flexible, scalable, and production-ready setup.With these steps, you’re ready to confidently build, deploy, and manage your Django app on AWS. Happy deploying! 🚀
Read more shuru tech blogs here