Deploying a two-tier app on AWS


In this blog, I’ll walk you through how I deployed a real-world two-tier application (frontend and backend) on AWS EC2, using foundational DevOps tools and AWS services like VPC, Security Groups, EC2 instances, and IAM.
This project gave me hands-on experience with how cloud infrastructure is designed and deployed in a scalable, secure, and modular way—just like in production environments.
Whether you’re a student like me or a beginner DevOps enthusiast, this guide will help you understand:
✅ Designing a two-tier architecture for the cloud
✅ Setting up secure networking using VPC and subnets
✅ Deploying and connecting frontend & backend services
✅ Configuring security groups, SSH access, and traffic flow
✅ Using key AWS services like EC2 and IAM effectively
Let’s dive in and bring this app to life on the cloud. ☁️🛠️
Creating Dockerfile for Python App
FROM python:3.10 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install mysqlclient
RUN pip install -r requirements.txt
COPY . .
# Stage 2
FROM python:3.10-slim
WORKDIR /app
COPY --from=builder /app .
RUN apt-get update && apt-get install -y \
build-essential \
default-libmysqlclient-dev \
python3-dev \
&& rm -rf /var/lib/apt/lists/*
EXPOSE 5000
CMD ["python", "app.py"]
Running MySql container as without it, python won’t run
docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD="root" mysql:latest
Now, this Python app and MySQL are unable to connect with each other.
So we need to connect them by keeping them in the same network.
Creating a network
docker network create two-tier
Kill both containers and provide both with network
If you see in app.py
So we need to include it in the command of building container of flask app, as it requires this
Creating flask container
docker run -d -p 5000:5000 --network=two-tier -e MYSQL_HOST=mysql -e MYSQL_USER=admin -e MYSQL_PASSWORD=admin -e MYSQL_DB=myDb two-tier-app:latest
Creating MySql container
docker run -d -p 3306:3306 --network=two-tier -e MYSQL_DATABASE=myDb -e MYSQL_USER=admin -e MYSQL_PASSWORD=admin -e MYSQL_ROOT_PASSWORD=admin mysql:latest
Inspect network - which apps are there in it
docker network inspect two-tier
Customizing Names
kill both container again
docker run -d -p 5000:5000 --network=two-tier -e MYSQL_HOST=mysql -e MYSQL_USER=admin -e MYSQL_PASSWORD=admin -e MYSQL_DB=myDb --name two-tier-app two-tier-app:latest
docker run -d -p 3306:3306 --network=two-tier -e MYSQL_DATABASE=myDb -e MYSQL_USER=admin -e MYSQL_PASSWORD=admin -e MYSQL_ROOT_PASSWORD=admin --name my-sql mysql:latest
Now running this command inside mySQL for creation of table
Now inside this, write this command
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
message TEXT
);
AND WOHOOO 🎉🎉🎉
App is available to use on port 5000 and data is going into the database
Pushing Docker image to Docker Hub
Do Docker login and enter docker hub credentials
Then tag the image
docker tag two-tier-app:latest dakshsawhneyy/two-tier-app:latest
And then push the image to docker hub
docker push dakshsawhneyy/two-tier-app:latest
Making Docker Compose for both the containers
version: '3.9'
services:
flask:
container_name: two-tier-app
image: dakshsawhneyy/two-tier-app
ports:
- "5000:5000"
# Add env variables from app.py
environment:
- MYSQL_HOST: "mysql"
- MYSQL_USER: "root"
- MYSQL_PASSWORD: "admin"
- MYSQL_DB: "myDb"
depends_on:
- mysql
mysql:
container_name: mysql
image: mysql:latest
ports:
- "3306:3306"
environment:
- MYSQL_DATABASE: "myDb"
- MYSQL_USER: "admin"
- MYSQL_PASSWORD: "admin"
- MYSQL_ROOT_PASSWORD: "admin"
As we were facing problem of tables, so we need to create volume here
Add this in mysql
volumes:
- ./message.sql:/docker-entrypoint-initdb.d/message.sql # table copy
- mysql-data:/var/lib/mysql # Data doesnt gets lost from the container
Final Code
version: '3.9'
services:
flask:
container_name: two-tier-app
image: dakshsawhneyy/two-tier-app
ports:
- "5000:5000"
# Add env variables from app.py
environment:
- MYSQL_HOST=mysql
- MYSQL_USER=root
- MYSQL_PASSWORD=admin
- MYSQL_DB=myDb
depends_on:
- mysql
mysql:
container_name: mysql
image: mysql:latest
ports:
- "3306:3306"
environment:
- MYSQL_DATABASE=myDb
- MYSQL_USER=admin
- MYSQL_PASSWORD=admin
- MYSQL_ROOT_PASSWORD=admin
volumes:
- ./message.sql:/docker-entrypoint-initdb.d/message.sql
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
And both containers are running using Docker-Compose 🎉🎉🎉
Kubernetes Architecture and Cluster Setup (Kubeadm)
Create two instances—t2.medium, i.e., one for master and one for worker node
Go inside (click here) kubestarer git repository
to see all download links
And Install by using commands
Creating Manifest Files
Making Deployment for Flask App
apiVersion: apps/v1
kind: Deployment
metadata:
name: flask-deployment
labels:
app: flask
spec:
replicas: 4
selector:
matchLabels:
app: flask
template:
metadata:
labels:
app: flask
spec:
containers:
- name: flask-app
image: dakshsawhneyy/two-tier-app
ports:
- containerPort: 5000
env:
- name: MYSQL_HOST
value: "mysql" # put mysql service ip address inside it
- name: MYSQL_USER
value: "root"
- name: MYSQL_PASSWORD
value: "admin"
- name: MYSQL_DB
value: "myDb"
If you want to scale deployment, use code
kubectl scale deployment flask-deployment --replicas=2
MySQL Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql-cnt
image: mysql:latest
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "admin"
- name: MYSQL_USER
value: "root"
- name: MYSQL_PASSWORD
value: "admin"
- name: MYSQL_DATABASE
value: "myDb"
Pod is crashing. We need to create pv and pvc
Create pv and, and app will run fine
Deploying two-tier-app using HELM
# Install HELM
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
Creating Nginx Pod using Helm
helm create nginx-chart
You can change anything in values. yml and charts gets updated by themselves
Let’s start deploying two-tier-app using HELM
Now modifying values
Now pasting environment vars of mysql into templates/deployment.yml
Inside template. yml, change these so we don’t need to hardcode the value
Then package Helm Chart
Now install chart
Error Solving
We havent provided 3306 inside security group inbound rules so we need to push 3306 inside inbound rules of master instance
Now use
helm list # verify which helm charts are present and delete the error one
helm uninstall mysql-chart
Also by mistake wrote image instead of env
Change username to admin from root
root is already built user and we say make a user root; it cannot make user of another name
so use username as “admin.”
Then Again install it
helm install mysql-chart ./mysql-chart
At least it’s running 😂. Now lets fix why it’s not working
And its's running🎉🎉🎉
Just needed to comment out the liveness probe inside values. yml
Creating flask-app
helm create flask-app-chart
Just like MySQL, copy env into values. yml and then extract them into templates. yml
Then go inside flask-app-chart/templates/service.yaml
And now package helm and install helm
Now going inside mysql to create a database
kubectl exec -it mysql-chart-7644667ccb-5vcd4 -- /bin/bash
And create database—naming it “myDb“ in it
And also inside mysql and database — for table
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
message TEXT
);
And paste worker url on browser with port 300001
And our site is running 🎉🎉🎉
And to uninstall all in one command
# just run this command
helm uninstall flask-app-chart mysql-chart
And all will be deleted
Deploying on EKS-Cluster
Make an EC2 “t2.micro” instance
Subscribe to my newsletter
Read articles from Daksh Sawhney directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Daksh Sawhney
Daksh Sawhney
Aspiring DevOps & DevSecOps Engineer | Automating Infra with Terraform & Ansible | Kubernetes Enthusiast | Building Scalable Pipelines 🤷♂️