Skip to content
← Back to blog
AWSNestJSAngularDevOpsDeployment

Deploy NestJS + Angular to AWS: Complete Guide

Firas Sayah·March 25, 2026·5 min read

Production-Ready AWS Architecture

SaaS Starter includes Terraform scripts and CI/CD pipelines for AWS. See the deployment feature.

Deploying a SaaS application to AWS requires more than just spinning up an EC2 instance. You need auto-scaling, managed databases, CDN distribution, and automated deployments. Here is the architecture that handles thousands of tenants reliably.

Architecture Overview

The production setup uses five core AWS services:

  • ECS Fargate — runs your NestJS API containers without managing servers
  • S3 + CloudFront — serves your Angular frontend globally with edge caching
  • RDS PostgreSQL — managed database with automated backups and failover
  • ElastiCache Redis — managed Redis for sessions, caching, and BullMQ queues
  • ALB — application load balancer with SSL termination

Deploying the Angular Frontend

Build your Angular app for production and upload to S3:

npx nx build web --configuration=production
aws s3 sync dist/apps/web s3://your-saas-frontend --delete
aws cloudfront create-invalidation --distribution-id EXXXXXX --paths "/*"

Configure CloudFront to serve index.html for all routes to support Angular routing:

{
  "CustomErrorResponses": [
    {
      "ErrorCode": 404,
      "ResponseCode": 200,
      "ResponsePagePath": "/index.html",
      "ErrorCachingMinTTLSeconds": 300
    }
  ]
}

Deploying the NestJS Backend

Create a Dockerfile optimized for production:

FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm nx build api --configuration=production

FROM node:20-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist/apps/api ./
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "main.js"]

Push to ECR and deploy to ECS:

aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URL
docker build -t saas-api .
docker tag saas-api:latest $ECR_URL/saas-api:latest
docker push $ECR_URL/saas-api:latest
aws ecs update-service --cluster saas --service api --force-new-deployment

Cloudrix SaaS Starter ships with this pre-configured and tested.

Skip weeks of boilerplate — auth, payments, multi-tenancy, and deployment included out of the box.

Try the live demo →

ECS Task Definition

{
  "family": "saas-api",
  "cpu": "512",
  "memory": "1024",
  "containerDefinitions": [
    {
      "name": "api",
      "image": "${ECR_URL}/saas-api:latest",
      "portMappings": [{ "containerPort": 3000 }],
      "environment": [
        { "name": "NODE_ENV", "value": "production" }
      ],
      "secrets": [
        { "name": "DATABASE_URL", "valueFrom": "arn:aws:ssm:..." },
        { "name": "JWT_SECRET", "valueFrom": "arn:aws:ssm:..." }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/saas-api",
          "awslogs-region": "eu-west-1"
        }
      }
    }
  ]
}

Auto-Scaling

Configure ECS auto-scaling based on CPU utilization:

aws application-autoscaling register-scalable-target \
  --service-namespace ecs \
  --resource-id service/saas/api \
  --scalable-dimension ecs:service:DesiredCount \
  --min-capacity 2 \
  --max-capacity 10

Set the scaling policy to add instances when CPU exceeds 70%:

aws application-autoscaling put-scaling-policy \
  --policy-name cpu-scaling \
  --service-namespace ecs \
  --resource-id service/saas/api \
  --scalable-dimension ecs:service:DesiredCount \
  --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration \
    '{"TargetValue": 70, "PredefinedMetricSpecification": {"PredefinedMetricType": "ECSServiceAverageCPUUtilization"}}'

RDS Configuration

Use a Multi-AZ deployment for production databases:

aws rds create-db-instance \
  --db-instance-identifier saas-db \
  --db-instance-class db.t3.medium \
  --engine postgres \
  --engine-version 16 \
  --master-username postgres \
  --multi-az \
  --storage-type gp3 \
  --allocated-storage 100 \
  --backup-retention-period 14

CI/CD with GitHub Actions

Automate deployments on every push to main:

name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy-api:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
      - run: |
          docker build -t saas-api .
          docker push $ECR_URL/saas-api:latest
          aws ecs update-service --cluster saas --service api --force-new-deployment

Cost Optimization

For an early-stage SaaS, this architecture costs approximately $150-300/month: Fargate tasks at $40-80, RDS at $50-100, ElastiCache at $25-50, and CloudFront at $10-20. As you scale, reserved instances and savings plans can reduce costs by 30-50%.

Get Started Today

All of these patterns are fully implemented in SaaS Starter. Skip 2-6 weeks of setup.

F

Firas Sayah

Senior Software Engineer

Full-stack developer with 5+ years building production SaaS applications with NestJS and Angular.