π Deploying a Full-Stack App on AWS with Terraform, ECS, and GitHub Actions β Step-by-Step Guide

Cloud deployments donβt have to be a nightmare of manual steps and unpredictable outcomes.
In this post, Iβll walk you through how I deployed a Dockerized full-stack application (FastAPI + Next.js) on AWS using ECS Fargate, Terraform for IaC, and GitHub Actions for automated CI/CD.
Whether youβre a developer or DevOps enthusiast, this guide will help you confidently move your apps from local to scalable cloud-native infrastructure.
π οΈ Tech Stack Used
Component | Tool/Service |
Frontend | Next.js |
Backend | FastAPI (Python) |
Containerization | Docker |
IaC | Terraform |
CI/CD | GitHub Actions |
Cloud Platform | AWS ECS Fargate, ALB, CloudWatch |
Secrets Manager | AWS Secrets Manager |
π§± 1. Dockerizing the Application
Both the frontend and backend apps are containerized using multi-stage Docker builds.
Example: FastAPI Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Next.js was built using the next build
command and served via next start
.
π 2. Setting Up GitHub Actions CI/CD
My GitHub Actions workflow performs:
Linting & Testing
Docker Build
Image Push to AWS ECR
Terraform Deployment
Key Features:
Automatic deployment on
main
pushGit SHA-based version tagging
Reusable secrets from GitHub and AWS
βοΈ 3. Infrastructure Provisioning with Terraform
Hereβs what Terraform provisions:
β VPC, Subnets, Internet Gateway
β ECS Cluster & Services (frontend & backend)
β ALB & Target Groups with routing rules
β IAM Roles and Policies
β CloudWatch Logs
β S3 backend for remote Terraform state
Example: Terraform ECS Service Snippet
resource "aws_ecs_service" "backend" {
name = "backend-service"
cluster = aws_ecs_cluster.main.id
launch_type = "FARGATE"
task_definition = aws_ecs_task_definition.backend.arn
desired_count = 1
load_balancer {
target_group_arn = aws_lb_target_group.backend.arn
container_name = "backend"
container_port = 8000
}
network_configuration {
subnets = var.private_subnets
security_groups = [aws_security_group.ecs.id]
assign_public_ip = true
}
}
π 4. Secrets Management
AWS Secrets Manager is used to store API keys and environment variables securely.
IAM roles are attached to ECS Task Definitions to fetch secrets securely without hardcoding them.
π 5. Monitoring & Alerts
CloudWatch dashboards for ECS service CPU utilization
CPU-based alarms trigger email notifications via SNS
ALB health checks on
/api/health
and/
routes
π§ͺ 6. Testing and Validation
Frontend tested using
@testing-library/react
Backend tested using
pytest
+httpx
clientHealth endpoints validated automatically in CI pipeline
Verified ALB routes via
curl
and browser
π§ Challenges I Faced (And Solved)
ALB 504 Gateway Timeout
π§ Fixed by updating security group to allow port 8000 and correcting health check path.Terraform State Conflicts
πͺ£ Solved using S3 backend with locking enabled via DynamoDB.Secrets Not Loading in Container
π Resolved by updating Task Role permissions and usingsecrets
block incontainer_definitions
.
β Final Result
You now have a fully automated, scalable, and secure deployment pipeline that:
Provisions infrastructure via Terraform
Pushes Docker images to ECR
Deploys to ECS with auto-scaling & load balancing
Monitors with alerts and secure secrets
π Next Steps
If you'd like a post on:
π GitOps and Helm integration
π Monitoring with Prometheus + Grafana
π‘ Cost optimization for AWS workloads
Let me know in the comments!
π Tags
#devops
#terraform
#aws
#githubactions
#docker
#ecs
#fullstack
Subscribe to my newsletter
Read articles from Joel Chandanshiv directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
