A production-style three-tier web application deployed on AWS ECS Fargate using Infrastructure as Code (CloudFormation) with a fully automated Jenkins CI/CD pipeline triggered by GitHub webhooks.
βββββββββββββββββββββββββββββββββββ
β Internet (User) β
ββββββββββββββββ¬βββββββββββββββββββ
β HTTP
ββββββββββββββββΌβββββββββββββββββββ
β Frontend ALB (Public) β
β internet-facing β
ββββββββββββββββ¬βββββββββββββββββββ
β Port 3000
βββββββββββββββββββββΌββββββββββββββββββββββ
β Frontend ECS Service β
β Node.js / Express β
β (Public Subnet - Fargate) β
βββββββββββββββββββββ¬ββββββββββββββββββββββ
β /api/* proxy
ββββββββββββββββΌβββββββββββββββββββ
β Backend ALB (Internal) β
β VPC-only β
ββββββββββββββββ¬βββββββββββββββββββ
β Port 5000
βββββββββββββββββββββΌββββββββββββββββββββββ
β Backend ECS Service β
β Node.js / Express REST API β
β (Private Subnet - Fargate) β
βββββββββββββββββββββββββββββββββββββββββββ
Three_Tier_AWS/
βββ Frontend/
β βββ app.js # Express server with API proxy
β βββ public/
β β βββ index.html # Product Catalog UI
β βββ package.json
β βββ Dockerfile
βββ Backend/
β βββ app.js # REST API (products CRUD)
β βββ package.json
β βββ Dockerfile
βββ cloudformation/
β βββ stack.yaml # Complete AWS infrastructure
βββ Jenkinsfile # CI/CD pipeline definition
βββ .gitignore
βββ README.md
| Layer | Technology |
|---|---|
| Frontend | Node.js, Express, HTML/CSS/JS |
| Backend | Node.js, Express REST API |
| Containerization | Docker |
| Container Registry | Amazon ECR |
| Compute | Amazon ECS Fargate |
| Load Balancing | Application Load Balancer (ALB) |
| Networking | VPC, Public/Private Subnets, NAT Gateway |
| Infrastructure | AWS CloudFormation |
| CI/CD | Jenkins (push-based GitHub webhook) |
| Source Control | GitHub |
Developer pushes code
β
βΌ
GitHub Repo
β webhook POST
βΌ
Jenkins Server
β
βββ Stage 1: Checkout code from GitHub
βββ Stage 2: Build Frontend Docker image
βββ Stage 3: Build Backend Docker image
βββ Stage 4: Push images to Amazon ECR
βββ Stage 5: Update ECS Frontend Service
βββ Stage 6: Update ECS Backend Service
βββ Stage 7: Verify deployment stable
Every git push to the main branch automatically triggers the full pipeline β no manual steps required.
The cloudformation/stack.yaml creates all AWS resources in a single deployment:
| Resource | Details |
|---|---|
| VPC | 10.0.0.0/16 with DNS enabled |
| Public Subnets | 2 AZs for Frontend ALB + Frontend ECS |
| Private Subnets | 2 AZs for Backend ECS |
| NAT Gateway | Allows private subnets to pull ECR images |
| Internet Gateway | Public internet access |
| Frontend ALB | Public-facing, routes to Frontend ECS |
| Backend ALB | Internal-only, routes to Backend ECS |
| ECS Cluster | Fargate-based, no EC2 to manage |
| Frontend Service | 1 task, port 3000 |
| Backend Service | 1 task, port 5000 |
| Security Groups | Strict least-privilege rules per tier |
| IAM Role | ECS Task Execution Role |
| CloudWatch Logs | 7-day retention for both services |
Internet β Frontend ALB SG (port 80)
Frontend ALB SG β Frontend ECS SG (port 3000)
Frontend ECS SG β Backend ALB SG (port 5000)
Backend ALB SG β Backend ECS SG (port 5000)
Each tier only accepts traffic from the tier directly above it.
- AWS CLI configured (
aws configure) - Docker installed
- Node.js installed
- Jenkins server running
git clone https://github.com/mayank4singh/Three_Tier_AWS.git
cd Three_Tier_AWSAWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
AWS_REGION=us-east-1
aws ecr create-repository --repository-name fullstack-frontend --region $AWS_REGION
aws ecr create-repository --repository-name fullstack-backend --region $AWS_REGION# Login to ECR
aws ecr get-login-password --region $AWS_REGION | \
docker login --username AWS --password-stdin \
$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
# Build and push Frontend
docker build -t fullstack-frontend ./Frontend
docker tag fullstack-frontend:latest \
$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/fullstack-frontend:latest
docker push \
$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/fullstack-frontend:latest
# Build and push Backend
docker build -t fullstack-backend ./Backend
docker tag fullstack-backend:latest \
$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/fullstack-backend:latest
docker push \
$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/fullstack-backend:latestaws cloudformation deploy \
--template-file cloudformation/stack.yaml \
--stack-name fullstack-app \
--region us-east-1 \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
FrontendImage=$AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/fullstack-frontend:latest \
BackendImage=$AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/fullstack-backend:latestaws cloudformation describe-stacks \
--stack-name fullstack-app \
--query 'Stacks[0].Outputs[?OutputKey==`AppURL`].OutputValue' \
--output textOpen the URL in your browser β your app is live! π
- Install plugins:
Git,GitHub Integration,Pipeline - Add credentials in Jenkins:
aws-access-key-idβ AWS Access Keyaws-secret-access-keyβ AWS Secret Key
- Create a Pipeline job:
- Definition:
Pipeline script from SCM - SCM: Git β
https://github.com/mayank4singh/Three_Tier_AWS.git - Branch:
*/main - Script Path:
Jenkinsfile
- Definition:
- Enable: β
GitHub hook trigger for GITScm polling
- Go to repo Settings β Webhooks β Add webhook
- Payload URL:
http://YOUR_JENKINS_IP:8080/github-webhook/ - Content type:
application/json - Events: β Just the push event
The frontend and backend communicate through the ALBs, not directly:
Browser
β GET /api/products
βΌ
Frontend ALB
β
βΌ
Frontend ECS (Express server)
β proxy: GET http://internal-backend-alb:5000/api/products
βΌ
Backend ALB (internal)
β
βΌ
Backend ECS (REST API)
β
βββ returns JSON
The BACKEND_URL environment variable is automatically injected by CloudFormation using !Sub "http://${BackendALB.DNSName}:5000". The frontend Express server proxies all /api/* requests to the backend β the browser never directly calls the backend ALB.
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /api/products |
List all products |
| GET | /api/products/:id |
Get single product |
| POST | /api/products |
Add new product |
| DELETE | /api/products/:id |
Delete product |
# Terminal 1 β Start Backend
cd Backend
npm install
node app.js
# Running on http://localhost:5000
# Terminal 2 β Start Frontend
cd Frontend
npm install
node app.js
# Running on http://localhost:3000Open http://localhost:3000 in your browser.
Built with β€οΈ and lots of β by Mayank Singh