Design a cost-optimized architecture for a startup scaling from 100 to 10M users
Quick Answer
Start with a monolithic serverless stack (API Gateway, Lambda, DynamoDB) for the first 100-10K users, then progressively decompose into microservices behind an ALB with ECS Fargate, introduce ElastiCache and read replicas at 100K users, and finally adopt a multi-region active-active architecture with Aurora Global Database, CloudFront, and reserved/Savings Plans pricing at 1M+ users.
Detailed Answer
Designing for startup scale is fundamentally about avoiding premature optimization while building in the right inflection points. Think of it like building a restaurant: you start with a food truck (serverless), move to a small storefront (single-region containers), then franchise (multi-region). At each stage, you only pay for what you need.
Phase 1 (0-10K users): Deploy everything serverless. API Gateway handles HTTP routing, Lambda runs your business logic, and DynamoDB provides single-digit millisecond reads. Your monthly bill stays under $50. The key insight is that Lambda's per-request pricing means zero cost at zero traffic. Use S3 for static assets behind CloudFront. Deploy with SAM or CDK for reproducibility. At this stage, a single DynamoDB table with a well-designed partition key (e.g., USER#12345) handles all access patterns through single-table design.
Phase 2 (10K-100K users): Lambda concurrency limits and cold starts begin hurting user experience. Migrate compute-heavy paths to ECS Fargate behind an Application Load Balancer. Keep event-driven workloads (image processing, notifications) on Lambda. Introduce ElastiCache Redis for session management and hot-path caching, reducing DynamoDB read costs by 60-70%. Add an SQS queue between your API tier and async processors to absorb traffic spikes. Your bill is now $500-2000/month, but you have headroom.
Phase 3 (100K-1M users): Migrate from DynamoDB to Aurora PostgreSQL with read replicas for complex query patterns. DynamoDB excels at key-value lookups but becomes expensive for analytics queries. Aurora Serverless v2 scales capacity automatically between 0.5 and 128 ACUs. Implement write-through caching: writes go to Aurora, which triggers a Lambda via DynamoDB Streams-style change capture to update ElastiCache. Deploy a NAT Gateway in each AZ (not one shared, which creates a single point of failure and cross-AZ data transfer charges).
Phase 4 (1M-10M users): Move to multi-region active-active with Aurora Global Database (under 1 second replication lag). Use Route 53 latency-based routing to direct users to the nearest region. Adopt CloudFront with Lambda@Edge for personalization at the edge. Purchase Compute Savings Plans (not EC2 Instance Savings Plans) because they cover Lambda, Fargate, and EC2, giving you flexibility as your compute mix evolves. Implement S3 Intelligent-Tiering for your data lake since access patterns are unpredictable at this scale. Use AWS Cost Explorer daily with anomaly detection alerts via SNS.
Production gotchas: Always set billing alarms at each phase boundary. NAT Gateway data processing charges are the number one surprise cost, often exceeding the compute bill. Use VPC endpoints for S3 and DynamoDB to eliminate NAT Gateway charges for AWS service traffic. Tag every resource with cost-allocation tags (team, environment, service) from day one. You cannot retroactively tag resources for cost attribution.