Domain-Integrated Flask App Deployment using Docker, Nginx, Route 53, ACM, and GitHub Actions

β PROJECT GOAL:
π Deploy a Flask web application on AWS EC2 using:
Docker + Docker Compose
Nginx (as reverse proxy and static file server)
AWS Application Load Balancer (ALB) for scalability and load distribution
GitHub Actions for CI/CD (auto-deploy on every push)
ACM (SSL) to secure traffic via HTTPS
Custom Domain
amayjaiswal.tech
π§ OVERALL STRUCTURE (Step-by-Step Overview)
Step | Task |
1 | Set up Flask App with Docker |
2 | Add Nginx as reverse proxy for Flask |
3 | Docker Compose setup |
4 | Push code to GitHub |
5 | Provision AWS Infrastructure: EC2 + ALB + Security Groups |
6 | Deploy app on EC2 (WSL2-friendly guide) |
7 | Setup Domain with Route 53 & HTTPS (ACM) |
8 | Configure GitHub Actions for CI/CD |
9 | Auto deployment via CI/CD after code push |
10 | Final testing and production-ready setup |
Weβll begin with Phase 1: Local Setup (Flask + Docker + Nginx)
I'll give you full code, directory structure, and commands.
β STEP 1: PROJECT DIRECTORY STRUCTURE
Create the following folder in your WSL2 terminal:
bashCopyEditmkdir flask-nginx-docker && cd flask-nginx-docker
# Create subfolders
mkdir app nginx
touch app/app.py app/Dockerfile
touch nginx/nginx.conf
touch docker-compose.yml
β STEP 2: Flask App Setup
π app/app.py
pythonCopyEditfrom flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "<h1>Welcome to Amay's Flask App via Nginx + Docker + AWS!</h1>"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
π app/Dockerfile
DockerfileCopyEdit# Stage 1 - Python base
FROM python:3.9-slim
WORKDIR /app
COPY . /app
RUN pip install flask gunicorn
EXPOSE 5000
CMD ["gunicorn", "-b", "0.0.0.0:5000", "app:app"]
β STEP 3: NGINX Reverse Proxy Setup
π nginx/nginx.conf
nginxCopyEditevents {}
http {
upstream flask_app {
server app:5000;
}
server {
listen 80;
location / {
proxy_pass http://flask_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
β STEP 4: Docker Compose Setup
π docker-compose.yml
yamlCopyEditversion: '3.8'
services:
app:
build: ./app
container_name: flask_app
expose:
- 5000
nginx:
image: nginx:latest
container_name: nginx_reverse_proxy
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
β STEP 5: Test Locally in WSL2
bashCopyEdit# Inside the root folder (flask-nginx-docker)
docker-compose up --build
Then go to:
http://localhost β You should see your Flask app served via NGINX π
β STEP 6: Push Code to GitHub
Create a new GitHub repo called
flask-nginx-docker
Run the following:
bashCopyEditgit init
git remote add origin https://github.com/YOUR_USERNAME/flask-nginx-docker.git
git add .
git commit -m "Initial commit - Flask + Docker + NGINX"
git push -u origin master
PHASE 2: AWS EC2 Deployment + Load Balancer + Custom Domain + HTTPS (ACM).
π§ We'll now host your Flask App (built in Phase 1) using Docker & Nginx on AWS EC2
π‘ Then scale with Application Load Balancer (ALB)
π Point custom domain (amayjaiswal.tech
) using Route 53
π Secure it with HTTPS using AWS ACM
β PHASE 2 OVERVIEW (Sections)
Step | Task |
1οΈβ£ | Provision AWS EC2 with Docker |
2οΈβ£ | Deploy Flask + Nginx App on EC2 |
3οΈβ£ | Setup ALB for Load Balancing |
4οΈβ£ | Register Domain with Route 53 |
5οΈβ£ | Point Domain to Load Balancer |
6οΈβ£ | Enable HTTPS using AWS ACM |
7οΈβ£ | (Later) Setup GitHub Actions for CI/CD |
β STEP 1: Provision EC2 Instance
π― Goal: Launch EC2 with Docker installed & allow required ports
π±οΈ A. Create EC2 Key Pair (If not already)
Go to AWS Console β EC2 β Key Pairs
Click Create key pair
Name:
cicd-keypair
File format:
.pem
Download and store it safely
π±οΈ B. Launch EC2 Instance (Amazon Linux 2)
- Go to EC2 Dashboard β Instances β Launch instance
Config:
Field | Value |
Name | flask-nginx-instance |
AMI | Amazon Linux 2 (64-bit) |
Instance type | t2.micro (free tier) |
Key pair | cicd-keypair |
Network | Default VPC |
Subnet | Any |
Auto-assign Public IP | Enable |
β Security Group:
Create new security group:
Allow SSH (port 22) β from your IP
Allow HTTP (port 80) β from Anywhere (0.0.0.0/0)
Allow HTTPS (port 443) β from Anywhere (0.0.0.0/0)
Click Launch Instance β
β C. Connect to EC2 via WSL2
bashCopyEdit# Go to folder where your .pem file is stored
chmod 400 cicd-keypair.pem
# Replace with your EC2 public IP:
ssh -i "cicd-keypair.pem" ec2-user@<YOUR_EC2_PUBLIC_IP>
β STEP 2: Install Docker, Git, Docker Compose on EC2
Run the following on your EC2:
bashCopyEdit# Update packages
sudo yum update -y
# Install Docker
sudo yum install docker -y
# Start and enable Docker
sudo systemctl start docker
sudo systemctl enable docker
# Add ec2-user to docker group
sudo usermod -aG docker ec2-user
# Install Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Check versions
docker --version
docker-compose --version
# Install Git
sudo yum install git -y
# Re-login to apply docker group (or just run commands with sudo for now)
β STEP 3: Clone Project on EC2 & Run It
bashCopyEdit# Clone your project from GitHub
git clone https://github.com/YOUR_USERNAME/flask-nginx-docker.git
cd flask-nginx-docker
# Start Docker containers
docker-compose up -d --build
π§ͺ Test: Visit http://<EC2_PUBLIC_IP>
β You should see your Flask app via NGINX
β STEP 4: Setup AWS Application Load Balancer (ALB)
π±οΈ A. Go to EC2 > Load Balancers > Create Load Balancer
Choose "Application Load Balancer"
Name:
flask-alb
Scheme: Internet-facing
IP Type: IPv4
Listeners: HTTP (80) and later HTTPS (443)
πΆ Network Mapping
VPC: Default
Select 2 Subnets in different Availability Zones (required for ALB)
π Security Group
Create or select a security group with ports:
HTTP (80)
HTTPS (443)
π Target Group
Name:
flask-target-group
Target type: Instance
Protocol: HTTP (port 80)
Register your EC2 instance in the target group
β Finish
Once ALB is created, you'll get a DNS name like:
CopyEditflask-alb-123456789.ap-south-1.elb.amazonaws.com
β Visit that URL and you should see your Flask app served from ALB.
β STEP 5: Connect Domain to Load Balancer (Route 53)
Since you purchased amayjaiswal.tech
from GitHub Student Pack (via Namecheap or similar):
π±οΈ A. Setup Hosted Zone
Go to AWS Route 53 β Hosted Zones β Create Hosted Zone
Domain name:
amayjaiswal.tech
Type: Public hosted zone
π±οΈ B. Update Name Servers
AWS will show 4 NS (Name Servers) after hosted zone is created
Go to your domain provider dashboard (Namecheap/GoDaddy)
Set Custom DNS β Paste those 4 NS records
β³ Wait for 5β30 mins for DNS propagation.
π±οΈ C. Create A Record in Route 53
Go to Hosted Zone β Create Record
Type: A Record
Name:
@
oramayjaiswal.tech
Routing: Alias
Target: Choose your ALB from the dropdown
Done β
β STEP 6: Enable HTTPS using AWS ACM
π±οΈ A. Go to AWS β ACM β Request a Certificate
Choose Public Certificate
Add domain:
amayjaiswal.tech
andwww.amayjaiswal.tech
Validation method: DNS validation
ACM will give CNAME records to add to Route 53 (auto-add if possible)
Wait until Status is "Issued"
π±οΈ B. Add HTTPS Listener to ALB
Go to Load Balancer β Listeners β Add Listener
Protocol: HTTPS, Port: 443
Choose ACM Certificate
Forward to
flask-target-group
Done! π
Now visit:
β
http://amayjaiswal.tech
β
https://amayjaiswal.tech
Your app is live on custom domain with HTTPS, served via ALB + Docker + Nginx + Flask!
β Step-by-Step: CI/CD Setup with GitHub Actions
What we will cover:
Create a GitHub Actions Workflow File (
deploy.yml
)Create IAM User with Permissions for EC2 and ALB Deployment
Configure AWS Secrets in GitHub
Connect GitHub Actions with Your Repository
β
Step 1: Create GitHub Actions Workflow File (deploy.yml
)
We will define a workflow that will:
Build your Docker image
Push to EC2 instance
Restart your Flask + Nginx app
Navigate to your GitHub repository where your Flask project is hosted.
Create a directory
.github/workflows/
Inside that directory, create a file called
deploy.yml
.
deploy.yml
Workflow File:
yamlCopyEditname: Deploy Flask App to EC2
on:
push:
branches:
- main # Trigger on push to main branch (change if you use another branch)
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Set up AWS CLI
run: |
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
- name: Configure AWS Credentials
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: "ap-south-1" # Update your region
run: |
aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
aws configure set region $AWS_REGION
- name: SSH into EC2 and deploy Docker container
uses: appleboy/ssh-action@v0.1.0
with:
host: ${{ secrets.EC2_PUBLIC_IP }} # EC2 Public IP
username: ec2-user
key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} # Store your private key in GitHub Secrets
script: |
cd flask-nginx-docker # Navigate to your project directory
docker-compose down # Stop running containers (if any)
docker-compose up -d --build # Build and start containers
- name: Restart Nginx on EC2
run: |
ssh -i "cicd-keypair.pem" ec2-user@${{ secrets.EC2_PUBLIC_IP }} "sudo systemctl restart nginx"
Breakdown:
Checkout Code: Fetches the latest code from your GitHub repository.
Set up AWS CLI: Installs AWS CLI on the runner to interact with AWS.
Configure AWS Credentials: Uses GitHub secrets to securely configure AWS credentials.
SSH into EC2: Connects to your EC2 instance via SSH, stops any existing Docker containers, and redeploys your updated app using
docker-compose
.Restart Nginx: Ensures Nginx restarts to serve the updated app.
β Step 2: Create IAM User with Permissions
For GitHub Actions to access AWS resources like EC2, we need an IAM user with the right permissions.
Go to IAM (Identity and Access Management) on AWS Console
Click on Users β Add user
User name:
github-actions
Access type: Programmatic access (for AWS CLI)
Attach Permissions: Select the following policies:
AmazonEC2FullAccess
(to manage EC2)AmazonS3FullAccess
(if you want to access S3, optional)AWSSecretsManagerReadWrite
(for secrets management, optional)ElasticLoadBalancingFullAccess
(to manage ALB)
Download Access Keys: After creating the user, note the Access Key ID and Secret Access Key.
β Step 3: Add AWS Secrets to GitHub
Now we need to securely store AWS credentials in your GitHub repository as secrets so that GitHub Actions can use them without exposing them in the code.
Go to your GitHub repository β Settings β Secrets β New repository secret
Add the following secrets:
AWS_ACCESS_KEY_ID
β Your AWS Access Key ID from IAMAWS_SECRET_ACCESS_KEY
β Your AWS Secret Access Key from IAMEC2_PUBLIC_IP
β Your EC2 public IP address (e.g.,ec2-XX-XX-XX-XX.ap-south-1.compute.amazonaws.com
)EC2_SSH_PRIVATE_KEY
β Your EC2 private key (the.pem
file, base64 encoded)
To base64 encode the .pem
key:
bashCopyEditbase64 cicd-keypair.pem > private_key_base64.txt
Then paste the content of private_key_base64.txt
into the EC2_SSH_PRIVATE_KEY
secret.
β Step 4: Test the Workflow
Push changes to the
main
branch (or any other branch specified in your GitHub Actions file).Go to GitHub Actions β You should see the workflow running.
- It will check out your code, set up AWS CLI, and SSH into your EC2 instance to redeploy your app.
β Step 5: Monitor and Debug
Check GitHub Actions logs to make sure everything runs smoothly.
If there are any issues with SSH or Docker commands, you can adjust the SSH script or check if your EC2 instance is accessible.
β Step-by-Step: Test GitHub Actions Deployment
π§ 1. Edit Your Flask App (app.py)
Open your project in VS Code or any editor youβre using, then update your app.py
file.
Update app.py
pythonCopyEditfrom flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Welcome to Amay's Flask App via Nginx + Docker + AWS! + Github Actions"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
β You only changed the return string of the root
/
route.
π¦ 2. Commit and Push the Changes to GitHub
Now commit this change and push it to your GitHub repo.
π§βπ» Run the following commands:
bashCopyEditgit add app.py
git commit -m "π Updated welcome message to test GitHub Actions deployment"
git push origin main
π Make sure you're on the
main
branch or the same branch you specified in your.github/workflows/deploy.yml
.
β 3. Go to GitHub β Actions Tab
Visit your repository on GitHub.
Click on the "Actions" tab.
You should see a new workflow "CI/CD Pipeline" running.
It will:
Checkout your code
Configure SSH access
SSH into your EC2 instance
Pull the latest code
Rebuild and restart the containers
π Wait for it to complete β takes about 1β2 mins.
π 4. Visit Your Domain / Public IP
Once the workflow completes:
Open your browser.
Visit:
http://<your-domain>
or
http://<your-EC2-public-ip>
You should now see:
vbnetCopyEditWelcome to Amay's Flask App via Nginx + Docker + AWS! + Github Actions
π Success Criteria
If you see the new updated message on your browser β CI/CD with GitHub Actions is working successfully!
You're now pushing code β GitHub β Deployed to EC2 with Docker & Nginx without touching the server manually π₯
Subscribe to my newsletter
Read articles from Amay Jaiswal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Amay Jaiswal
Amay Jaiswal
AWS Cloud & DevOps Engineer | Cloud Computing | Linux | Terraform & CloudFormation | AWS (EC2, S3, Lambda, API Gateway, DynamoDB, IAM) | Docker | Jenkins | CI/CD Pipelines | MySQL | Java | Jira | Postman | Git/GitHub