Deploy NestJS + Angular to AWS: Complete Guide
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.
- Free version: GitHub
- Pro ($249): Get SaaS Starter Pro