AWS for a small team: boring on purpose, 35% cheaper
TL;DR - For a multi-tenant platform run by a small team, I picked AWS services the team can operate at 3 a.m.: ECS with auto scaling, RDS, SQS/SNS, CloudFront and CloudWatch alarms. Then I went after the bill with four levers (rightsizing, Savings Plans, Spot for workers and turning off non-production at night) and cut the monthly cost by 35%.
The platform I lead is a multi-tenant asset management system. It integrates with customers’ ERPs and with public-sector systems, and it has a mobile app used in the field. The team is small and everyone touches backend, frontend and mobile. Nobody has time to babysit a Kubernetes cluster.
So the first question for the architecture wasn’t “what’s the best stack”. It was “who fixes this when it breaks, and how fast”.
What I chose and why
- ECS with auto scaling for the APIs and workers. No cluster to manage, deploy is a new task definition, rollback is the previous one.
- RDS PostgreSQL with automated backups. One engine, one set of skills.
- SQS and SNS between the API and anything slow: ERP and public-sector integrations, report generation, bulk imports. The API answers fast and the worker takes its time. SNS fans one event out to the consumers that care about it.
- CloudFront in front of the web app and static files.
- CloudWatch alarms on what wakes someone up: queue age, 5xx rate, task restarts, database CPU and connections.
flowchart LR
Users[Web and mobile] --> CF[CloudFront]
CF --> ALB[Load balancer]
ALB --> API[API on ECS, auto scaling]
API --> RDS[(RDS PostgreSQL)]
API --> SNS[SNS topics]
SNS --> SQS[SQS queues]
SQS --> Workers[Workers on ECS]
Workers --> Ext[ERP and public-sector systems]
CW[CloudWatch alarms] -.-> API
CW -.-> SQS
The rule that shaped everything: anything that depends on a system we don’t control goes through a queue. ERPs and government systems go down, get slow and change behavior without notice. If the API called them directly, their bad day would become our bad day. With a queue in the middle, a slow integration means a growing queue and a CloudWatch alarm, not a slow app for every tenant.
Cutting the bill by 35%
Once the platform was stable, I looked at the bill line by line. There was no single big fix. It was four boring ones:
- Rightsizing. Most tasks and the database were sized for a peak that never came. I looked at CPU and memory over a few weeks in CloudWatch and cut what was idle.
- Savings Plans for the baseline that runs all the time. Once the size is right, committing to it is free money.
- Spot for workers. Queue consumers can be interrupted and retried, so they are the right place for Spot capacity. The API stays on regular capacity.
- Turning off non-production at night and on weekends. Staging and dev don’t need to run when nobody is working.
The order matters. Commit to Savings Plans before rightsizing and you lock in the waste.
What I’d tell another tech lead
Pick the architecture your team can debug, put a queue in front of every external system, and alarm on queue age before anything else. Then read your bill like you read your logs. The savings were there the whole time; nobody had looked.