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

Amay JaiswalAmay Jaiswal
11 min read

βœ… 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)

StepTask
1Set up Flask App with Docker
2Add Nginx as reverse proxy for Flask
3Docker Compose setup
4Push code to GitHub
5Provision AWS Infrastructure: EC2 + ALB + Security Groups
6Deploy app on EC2 (WSL2-friendly guide)
7Setup Domain with Route 53 & HTTPS (ACM)
8Configure GitHub Actions for CI/CD
9Auto deployment via CI/CD after code push
10Final 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

  1. Create a new GitHub repo called flask-nginx-docker

  2. 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)

StepTask
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)

  1. Go to AWS Console β†’ EC2 β†’ Key Pairs

  2. Click Create key pair

    • Name: cicd-keypair

    • File format: .pem

    • Download and store it safely


πŸ–±οΈ B. Launch EC2 Instance (Amazon Linux 2)

  1. Go to EC2 Dashboard β†’ Instances β†’ Launch instance

Config:

FieldValue
Nameflask-nginx-instance
AMIAmazon Linux 2 (64-bit)
Instance typet2.micro (free tier)
Key paircicd-keypair
NetworkDefault VPC
SubnetAny
Auto-assign Public IPEnable

βœ… 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

  1. Choose "Application Load Balancer"

  2. Name: flask-alb

  3. Scheme: Internet-facing

  4. IP Type: IPv4

  5. 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

  1. Go to AWS Route 53 β†’ Hosted Zones β†’ Create Hosted Zone

πŸ–±οΈ B. Update Name Servers

  1. AWS will show 4 NS (Name Servers) after hosted zone is created

  2. Go to your domain provider dashboard (Namecheap/GoDaddy)

  3. 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: @ or amayjaiswal.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 and www.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

  1. Go to Load Balancer β†’ Listeners β†’ Add Listener

  2. Protocol: HTTPS, Port: 443

  3. Choose ACM Certificate

  4. 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:

  1. Create a GitHub Actions Workflow File (deploy.yml)

  2. Create IAM User with Permissions for EC2 and ALB Deployment

  3. Configure AWS Secrets in GitHub

  4. 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

  1. Navigate to your GitHub repository where your Flask project is hosted.

  2. Create a directory .github/workflows/

  3. 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.

  1. Go to IAM (Identity and Access Management) on AWS Console

  2. Click on Users β†’ Add user

    • User name: github-actions

    • Access type: Programmatic access (for AWS CLI)

  3. 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)

  4. 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.

  1. Go to your GitHub repository β†’ Settings β†’ Secrets β†’ New repository secret

  2. Add the following secrets:

    • AWS_ACCESS_KEY_ID β†’ Your AWS Access Key ID from IAM

    • AWS_SECRET_ACCESS_KEY β†’ Your AWS Secret Access Key from IAM

    • EC2_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

  1. Push changes to the main branch (or any other branch specified in your GitHub Actions file).

  2. 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 πŸ’₯

1
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