Building Scalable Applications with Next.js and AWS
By Carlos Diaz · December 8, 2024 · 3 min read
The question I get asked most often about scaling Next.js applications is not about Next.js at all - it is about which AWS services should sit behind it and when to introduce each one. In this post I want to walk through the actual sequence of decisions rather than the theoretical menu of options.
For most applications, the right starting point is Lambda behind API Gateway, backed by a managed database like DynamoDB or Aurora Serverless, with CloudFront caching static assets and API responses where possible. This gets you to production fast, scales without capacity planning, and keeps costs proportional to actual usage instead of provisioned capacity sitting idle. The mistake I see most often is teams reaching for Kubernetes on day one because it is the 'proper' way to run production software, when a serverless-first approach would have gotten them to their first thousand users with a fraction of the operational overhead.
Caching is where the architecture actually earns its scalability. CloudFront in front of both static assets and cacheable API routes removes the majority of read traffic from your origin before it ever reaches a Lambda function. ElastiCache (Redis) handles the harder case: data that changes often enough that a CDN TTL will not work, but is expensive enough to compute or fetch that recomputing it per request would create unnecessary load on the database. Getting the invalidation strategy right - versioned cache keys, targeted invalidation instead of broad flushes - matters more than the caching layer itself.
The migration point from serverless to containers is not a fixed threshold, it is a symptom: cold starts becoming a real latency problem for a specific hot path, a workload with cost characteristics that no longer make sense at high, sustained volume, or a dependency that genuinely needs a long-running process. When that happens, I move only that specific service to a container running on ECS Fargate, rather than migrating the whole platform, keeping the cost and operational benefits of serverless everywhere else.
Observability has to be part of the architecture from the start, not bolted on afterward. CloudWatch metrics and structured logs, paired with distributed tracing once you have more than a couple of services talking to each other, are what let you make these migration decisions based on real data instead of guesses about where the bottleneck is.
Key Takeaways
- Start serverless-first with Lambda, API Gateway, and a managed database for most endpoints.
- CloudFront and ElastiCache caching cuts latency and cost before requests reach your origin.
- Move to containers only for the specific hot paths that genuinely need them, not the whole platform.
- Getting cache invalidation right matters more than the caching layer itself.
- Observability has to be designed in from the start to make migration decisions based on data, not guesses.

