Mark Lowel Montealto — Full Stack Developer & DevOps Engineer

Mark Lowel
Montealto

Full Stack Developer & DevOps Engineer

Blog

April 2026

·

3 min read

Deploy a Full-Stack App on AWS: ECS, RDS & CloudFront

Deploy a containerised full-stack app on AWS using ECS Fargate for compute, RDS for the database, and CloudFront as a CDN — infrastructure you can actually maintain.

Introduction

  • The goal: a production-grade AWS architecture for a full-stack app (e.g., Next.js frontend + Laravel API).
  • Components: ECS Fargate (containers), ECR (image registry), RDS PostgreSQL (database), CloudFront (CDN), ALB (load balancer), ACM (SSL), Route 53 (DNS).
  • Prerequisite: Docker images built and working locally; AWS CLI configured.

Architecture Overview

Browser → Route 53 → CloudFront → ALB → ECS Fargate Tasks → RDS PostgreSQL

                           (static assets cached at CloudFront edge)
  • CloudFront caches static assets and forwards API requests to ALB.
  • ECS Fargate runs containerised app tasks — no EC2 instances to manage.
  • RDS in a private subnet — only reachable from ECS tasks via security groups.

Step 1: Push Docker Images to ECR

# Create ECR repositories
aws ecr create-repository --repository-name my-app/frontend
aws ecr create-repository --repository-name my-app/api
 
# Authenticate and push
aws ecr get-login-password --region ap-southeast-1 | \
  docker login --username AWS --password-stdin <account>.dkr.ecr.ap-southeast-1.amazonaws.com
 
docker build -t my-app/frontend .
docker tag my-app/frontend:latest <account>.dkr.ecr.ap-southeast-1.amazonaws.com/my-app/frontend:latest
docker push <account>.dkr.ecr.ap-southeast-1.amazonaws.com/my-app/frontend:latest

Step 2: VPC and Networking

  • Use a VPC with public subnets (ALB, NAT Gateway) and private subnets (ECS tasks, RDS).
  • Security groups:
    • ALB SG: inbound 443 from 0.0.0.0/0.
    • ECS SG: inbound from ALB SG only.
    • RDS SG: inbound 5432 from ECS SG only.
  • Use Terraform or AWS CloudFormation to manage networking as code (see Terraform article).

Step 3: RDS PostgreSQL Setup

  • Engine: PostgreSQL 16, instance db.t4g.micro (free-tier eligible).
  • Multi-AZ: enabled for production (automatic failover).
  • Subnet group: private subnets.
  • Store credentials in AWS Secrets Manager; inject into ECS task as environment variables.

Step 4: ECS Cluster and Task Definitions

  • Create an ECS cluster (Fargate launch type).
  • Task definition: specify CPU (256–1024), memory, container image URI, port mappings, environment variables, and log driver (awslogs).
{
  "containerDefinitions": [{
    "name": "frontend",
    "image": "<account>.dkr.ecr.ap-southeast-1.amazonaws.com/my-app/frontend:latest",
    "portMappings": [{ "containerPort": 3000 }],
    "environment": [
      { "name": "NEXT_PUBLIC_API_URL", "value": "https://api.myapp.com" }
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": { "awslogs-group": "/ecs/frontend", "awslogs-region": "ap-southeast-1" }
    }
  }]
}

Step 5: Application Load Balancer

  • Create an ALB in public subnets with HTTPS listener (ACM certificate from *.myapp.com).
  • Target group: ip target type pointing to ECS tasks.
  • Health check: GET /api/health → expect 200.
  • Redirect HTTP → HTTPS at the listener level.

Step 6: ECS Service

  • Create an ECS Service that maintains desired count (2+ tasks for HA) using the task definition.
  • Rolling deployment: minimumHealthyPercent: 100, maximumPercent: 200 — zero-downtime deploys.
  • Auto Scaling: scale out when CPU > 70%, scale in when CPU < 30%.

Step 7: CloudFront Distribution

  • Origin 1: ALB — for dynamic requests (/api/*, server-rendered pages).
  • Origin 2: S3 — for static assets (if using S3 for uploads).
  • Cache behaviour: cache static assets (/_next/static/*) aggressively (TTL 31536000); forward all headers for dynamic paths.
  • SSL: ACM certificate, redirect HTTP to HTTPS.
  • Custom domain: CNAME record in Route 53 pointing to CloudFront.

Step 8: CI/CD Integration

  • On merge to main: build Docker image → push to ECR → trigger ECS rolling deploy.
  • GitHub Actions step: aws ecs update-service --force-new-deployment.
  • Use OIDC for GitHub → AWS IAM role (no long-lived access keys).

Cost Estimates (ap-southeast-1)

ServiceConfigMonthly estimate
ECS Fargate2 tasks × 0.25 vCPU / 0.5 GB~$15
RDS db.t4g.microSingle-AZ~$15
ALB1 LCU estimate~$20
CloudFront10 GB transfer~$1
NAT GatewayLow traffic~$35

Conclusion

  • ECS Fargate + RDS + CloudFront is the modern AWS baseline for containerised apps — no EC2 patching, no Kubernetes overhead.
  • NAT Gateway cost is usually the surprise; consider VPC endpoints for ECR/Secrets Manager to reduce NAT traffic.
  • Next steps: add AWS WAF in front of CloudFront, enable RDS Multi-AZ, and codify everything in Terraform.