← Blog/Deploying Angular + FastAPI on AWS Fargate: From Zero to Production
DevOps

Deploying Angular + FastAPI on AWS Fargate: From Zero to Production

May 24, 2026·4 min read
Med Amine Mahmoud
Med Amine Mahmoud
Founder and Editor, Smash The Exam
Reviewed: 2026-05-26 · LinkedIn

Deploying Angular + FastAPI on AWS Fargate: From Zero to Production focuses on what actually matters in practice: decision context, safe rollout steps, and verification points.

AWSDevOpsAngularDocker

Deploying an Angular 19 + FastAPI App on AWS Fargate: From Zero to Production

Consolidated from real engineering sessions spanning April–May 2026. All secrets, account IDs, and internal endpoints replaced with placeholders.

Delivery Focus 1: Where this architecture earns its value for predictable operations (Deploying Angular Fastapi)

This article documents the complete journey of deploying a full-stack web application (Angular 19 SSR frontend + FastAPI backend + nginx reverse proxy) on AWS ECS Fargate — from initial CloudFormation stack creation through SSL/domain setup, autoscaling, and production hardening.


Editorial review note for Deploying Angular Fastapi

This section was reviewed by a human editor to keep the recommendations actionable and technically grounded. Reviewed by: Med Amine Mahmoud. Last editorial review: 2026-05-26T16:10:01Z.

Delivery Focus 3: How to avoid expensive rework for cleaner ownership (Deploying Angular Fastapi)

Steps

  1. Register domain and create Route 53 hosted zone
  2. Request ACM certificate (DNS validation via Route 53 CNAME records)
  3. Create Route 53 A-record aliases pointing example.com and www.example.com to ALB
  4. Add HTTPS listener on ALB port 443 with TLS 1.3 security policy
  5. Change HTTP listener from forward to 301 redirect to HTTPS
  6. Open port 443 in ALB security group
  7. Update CORS origins in backend from * to specific HTTPS domains
# Add HTTPS listener
aws elbv2 create-listener `
--load-balancer-arn $ALB_ARN `
--protocol HTTPS --port 443 `
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 `
--certificates CertificateArn=$ACM_CERT_ARN `
--default-actions Type=forward,TargetGroupArn=$TG_ARN

# Modify HTTP to redirect
aws elbv2 modify-listener `
--listener-arn $HTTP_LISTENER_ARN `
--default-actions "Type=redirect,RedirectConfig={Protocol=HTTPS,Port=443,StatusCode=HTTP_301}"

Result

  • https://www.example.com — fully functional with SSR, security headers, gzip
  • http://example.com — 301 redirect to HTTPS
  • All security headers present (HSTS, CSP, X-Frame-Options, etc.)

Delivery Focus 4: Where teams usually get this wrong for measurable outcomes (Deploying Angular Fastapi)

Before (Score: 1/10)

  • Single task, single AZ, no auto-scaling
  • No deployment circuit breaker
  • No per-container resource limits
  • Estimated capacity: ~30 concurrent users before degradation

After (Score: 9/10)

# Auto Scaling configuration
ScalableTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MinCapacity: 1
MaxCapacity: 8
ResourceId: !Sub 'service/${ECSCluster}/${ECSService}'
ScalableDimension: ecs:service:DesiredCount

CPUScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyType: TargetTrackingScaling
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 60.0

RequestCountScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyType: TargetTrackingScaling
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ALBRequestCountPerTarget
TargetValue: 1000.0

Key Changes

  • Task size: Upgraded to 2 vCPU / 4 GB (later right-sized back to 1 vCPU / 2 GB)
  • Auto Scaling: min=1, max=8, CPU target 60%, request target 1000
  • Circuit Breaker: Enabled with auto rollback
  • Container Insights: Enabled for per-container metrics

Delivery Focus 5: The practical decision path for fewer incident surprises (Deploying Angular Fastapi)

Architecture Decision: Single ALB with Host-Based Routing

Instead of a separate ALB for dev ($16/month), we consolidated:

Single ALB
├── Rule: Host=www.example.com → prod target group
├── Rule: Host=dev.example.com + SourceIP=<YOUR_IP>/32 → dev target group
└── Default: 403 Forbidden (blocks direct ALB access)

Monthly savings: ~$16/month by eliminating the second ALB.

Dev Auto-Shutdown (Save ~$18/month idle costs)

Lambda-based auto-shutdown after 30 minutes of zero traffic:

CloudWatch Alarm (RequestCount = 0 for 30 min)
→ SNS Topic
→ Lambda (scales ECS service to 0)
→ Email notification

Delivery Focus 6: How to execute without guesswork for this workload (Deploying Angular Fastapi)

ALB Direct Access Blocking

  • Default action on both HTTP/HTTPS listeners returns 403
  • Only host-header matching rules forward traffic
  • Result: ALB DNS name returns 403, domain names return 200

CSP Header Gotcha

Adding upgrade-insecure-requests to Content-Security-Policy caused a complete blank page when serving over HTTP. The directive tells browsers to upgrade all subresource loads to HTTPS, but since the ALB initially served HTTP only, all JS/CSS assets failed to load.

Lesson: Never add upgrade-insecure-requests to CSP until HTTPS is fully configured end-to-end.

Database Migration Strategy

ECS Fargate with SQLAlchemy requires careful migration handling:

  • create_tables() only creates new tables, doesn't alter existing ones
  • Column additions need explicit ALTER TABLE in a migration function
  • The app's _migrate_columns() function handles schema drift on startup

Delivery Focus 7: What to validate before shipping for your runbook (Deploying Angular Fastapi)

LessonContext
Deploy with DesiredCount=0 firstCreates infra without needing images ready
Angular SSR needs ALLOWED_HOSTS=* in ECSALB health checks use private IPs
CSP upgrade-insecure-requests breaks HTTPOnly add after HTTPS is fully configured
Single ALB with host routing saves $16/moConsolidate dev/prod behind one ALB
Auto-shutdown Lambda saves idle costs30-min idle → scale to 0
Circuit breaker prevents bad deploy loopsEnable DeploymentCircuitBreaker
Container Insights needed for debuggingNo per-container metrics without it

Delivery Focus 8: Tradeoffs that matter in production for production readiness (Deploying Angular Fastapi)

ResourceMonthly Cost% of Total
ECS Fargate (1 vCPU / 2 GB)~$3643%
ALB + Public IPs~$2732%
RDS (db.t3.micro)~$1518%
Other (Secrets, Logs, ECR)~$67%
Total~$84/month

With optimizations (Spot, ARM64, reserved): ~$70-85/month projected.

Delivery Focus 9: Implementation details that change outcomes for sustained reliability (Deploying Angular Fastapi)

Architecture

Internet → ALB (port 80/443) → ECS Fargate Task
├── nginx (reverse proxy)
├── frontend (Angular SSR, port 4000)
└── backend (FastAPI, port 8000)
→ RDS PostgreSQL (db.t3.micro)

CloudFormation Strategy

The initial deployment used a zero-count deploy strategy:

  1. Deploy CloudFormation with DesiredCount: 0 — creates all infrastructure without needing container images
  2. Build Docker images locally while stack creates
  3. Push images to ECR once repos are available
  4. Update service to DesiredCount: 1
# Key CloudFormation resources
Resources:
ECSCluster:
Type: AWS::ECS::Cluster
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Cpu: '1024'
Memory: '2048'
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
ContainerDefinitions:
- Name: nginx
Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/myapp-nginx:latest'
PortMappings: [{ContainerPort: 80}]
Essential: true
- Name: frontend
Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/myapp-frontend:latest'
Essential: true
- Name: backend
Image: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/myapp-backend:latest'
Essential: true

Critical Issue: Angular SSR Host Rejection

After deployment, the ALB health checks returned 500 errors. Root cause: Angular SSR's CommonEngine was rejecting requests from the ALB's private IP because the Host header didn't match allowed hosts.

Fix: Add ALLOWED_HOSTS=* environment variable to disable host checking in the ECS task definition:

// server.ts — handle ALLOWED_HOSTS
const allowedHosts = process.env['ALLOWED_HOSTS'];
if (allowedHosts === '*') {
// Skip host validation for ALB health checks
}