AWS Architecture
What gets deployed when you run infra:deploy on AWS and how traffic flows through the system.
High-Level Architecture
Traffic Flows
Compute: ECS Fargate
All containerized services run on ECS Fargate in private VPC subnets:
- Backend services (NestJS): Not behind the load balancer. Kong calls them over Cloud Map inside the VPC
- Next.js frontends: Routed via host-based rules on the ALB listener (port 443)
- Kong Gateway: Catch-all on port 443 for API traffic
- Workers: Background job processors, no load balancer
- Service discovery via Cloud Map (
{service}.{project}.local) - Auto-scaling based on CPU utilization (target 70%) between
minInstancesandmaxInstances. Every service, Kong included, runs at least one task: AWS has no scale-to-zero, andminInstances: 0fails validation
Next.js services must expose a /health route (included in the framework template by default).
Data: RDS + ElastiCache
RDS PostgreSQL 16:
- Separate databases per service (auth_db, offers_db, kong_db)
- Private subnets only (no public access)
- Tiers:
db.t3.micro(dev) throughdb.r6g.large(prod) - Multi-AZ: No (dev) / Yes (prod)
- Automated backups with 7-day retention
ElastiCache Redis 7:
- Rate limiting, session cache, BullMQ queues
- Private subnets only, password required, encryption at rest + in transit
- Tiers:
cache.t3.micro(dev) throughcache.r6g.large(prod)
Storage: S3
When storage buckets are configured in config.json, Terraform creates S3 buckets:
- One bucket per logical name:
{project}-{name}-{env}-{accountId}(e.g.,tsdevstack-uploads-dev-123456789012) - Account ID suffix ensures global uniqueness across S3's global namespace
- Block all public access enabled
- Server-side encryption (AES-256)
- CORS set to
*(presigned URLs are the security boundary)
IAM: Each ECS task role gets s3:GetObject, s3:PutObject, s3:DeleteObject, and s3:ListBucket permissions on all storage buckets.
No access keys in containers — ECS tasks use IAM Task Roles automatically.
For setup and usage, see Object Storage.
Edge: CloudFront + ALB + WAF
CloudFront:
- Separate distributions for API, Next.js, and each SPA
- TLS termination, edge caching, HTTP/2
- Sends
X-Origin-Verifyheader to prevent origin bypass
ALB (Application Load Balancer):
- 2 listeners:
- HTTP:80 > Redirect to HTTPS
- HTTPS:443 > External traffic (host-based routing: Next.js domains > ECS, catch-all > Kong, validates
X-Origin-Verify)
- Target groups for Kong and each Next.js frontend only. Backend services have no target group and no listener: nothing on the internet can reach them without going through Kong
AWS WAF:
- AWS Managed Rules: Common Rule Set, SQLi Rule Set, Known Bad Inputs, IP Reputation List, Anonymous IP List, Linux Rule Set
- Custom rules for Node.js prototype pollution and
child_processinjection - SSRF protection (blocks metadata endpoint 169.254.169.254)
- Rate limiting: 1000 requests per 60 seconds per IP (configurable via
infrastructure.json, translated to 5000 per 5-minute AWS window) - Custom rules via byte match, rate-based, and geo match statements in
infrastructure.json - See Service Configuration — WAF Rules for configuration details
Access Logging:
- ALB access logs to S3 bucket (ELB service principal policy, 90-day lifecycle)
- CloudFront standard logs to S3 bucket (per-distribution prefixes, 90-day lifecycle)
- WAF logs to CloudWatch Log Groups (
aws_wafv2_logging_configurationfor CloudFront WAF)
Networking
Service Discovery
AWS uses a dual approach:
infra:deploy writes each service's Cloud Map URL to Secrets Manager ({SERVICE}_URL), and infra:build-kong resolves the Kong config's service URLs from those secrets. Kong also fetches the auth-service's OIDC discovery document and signing keys over Cloud Map.
No Scale-to-Zero
Fargate cannot hold a request while a stopped task starts, and backends are not reachable from outside the VPC, so there is nothing that could wake a service on demand. Every ECS service runs at least one task; minInstances: 0 fails validation with a hint. Services scale up and down on CPU between minInstances and maxInstances.
This keeps AWS on the same model as GCP and Azure: private backends, Kong as the only way in. It does cost more in development than scale-to-zero would; see AWS Cost Estimation. If you need scale-to-zero for development environments, GCP and Azure support it natively.
SPA Deployment
SPAs deploy to S3 with CloudFront:
- S3 bucket: private (no public access), versioning enabled
- CloudFront OAC (Origin Access Control) with SigV4 signing
- 403/404 errors redirect to
index.htmlfor client-side routing
Secrets: Secrets Manager
Secrets are injected into ECS task definitions as environment variables via ARN references.
Cost Estimation
See AWS Cost Estimation for a detailed breakdown across development, production, and scaled scenarios, with links to official AWS pricing pages.
Async Messaging
Async messaging uses Redis Streams on the same ElastiCache instance used for caching and BullMQ. No additional AWS resources are created — messaging is a framework-level feature that runs on existing infrastructure. See Async Messaging for details.
Terraform Resources
AWS deployments create approximately 45 Terraform resources including VPC, subnets, NAT gateways, security groups, ALB with target groups, ECS cluster and services, auto-scaling policies, RDS instance, ElastiCache cluster, CloudFront distributions, WAF rules, Route 53 records, and IAM roles. Scheduled jobs add EventBridge schedules and a job invoker Lambda.