Skip to content
← All posts
074 min read

How I Scaled My Next.js + Node.js App to 10,000 Users on AWS

  • nodejs
  • nextjs
  • aws
How I Scaled My Next.js + Node.js App to 10,000 Users on AWS — by Chirag Mehta
How I Scaled My Next.js + Node.js App to 10,000 Users on AWS

We hit scaling issues at 500 users. Here’s the exact sequence of what broke, what we fixed, and what the architecture looks like now.

PUBLISH METADATA

  • Tags: AWS, Next.js, Node.js, Scalability, Software Engineering
  • Description: A real-world story of scaling a Next.js + Node.js SaaS from 500 to 10,000 concurrent users on AWS — covering the breaking points, fixes applied at each stage, and the final architecture.
  • Suggested publish date: Week 3 — as a “story” format for virality
  • Suggested cover image: Growth chart going up sharply. Next.js + Node.js + AWS logos. “500 → 10K users” text overlay.

Introduction

When we launched, the architecture was dead simple: one Next.js app on Vercel, one Node.js API on a single EC2 t3.medium, one MongoDB Atlas M10.

It worked great up to about 500 concurrent users. Then things started breaking.

This is the honest story of what broke at each scale milestone and exactly what we changed. No marketing speak. Real numbers.

The Starting Architecture (and Its Breaking Points)

The Starting Architecture (and Its Breaking Points)
The Starting Architecture (and Its Breaking Points)
Users → Vercel (Next.js) → EC2 t3.medium (Node API) → MongoDB Atlas M10

500 users: EC2 CPU hits 95% during business hours. Response times spike to 8–12 seconds. Some requests time out.

Root cause: Node.js is single-threaded. One slow database query blocks everything.

Fix #1: Horizontal Scaling with ECS Fargate (500 → 2,000 users)

Moved the Node.js API from single EC2 to ECS Fargate with 3 tasks behind an ALB.

Users → Vercel (Next.js) → ALB → ECS Fargate (3 tasks) → MongoDB Atlas M10

Immediate result: Response times dropped from 8s to 800ms. CPU spread across 3 containers.

Cost change: EC2 t3.medium (33/month)→3xFargate0.25vCPU/512MB(45/month). Small increase for massive stability gain.

Fix #2: Database Connection Pooling (2,000 → 3,500 users)

At 2,000 users, MongoDB started throwing “Too many connections” errors. ECS was now creating 3x the connections the old EC2 did.

The fix: Added MongoDB connection pooling at the application layer.

// Before — new connection per request (terrible)
async function getDb() {
return mongoose.createConnection(process.env.MONGODB_URI)
}
// After — shared pool
let cachedConnection = null
async function getDb() {
if (cachedConnection) return cachedConnection
cachedConnection = await mongoose.connect(process.env.MONGODB_URI, {
maxPoolSize: 10, // Max 10 connections per container
minPoolSize: 2, // Keep 2 warm
maxIdleTimeMS: 30000, // Close idle connections after 30s
serverSelectionTimeoutMS: 5000,
})
return cachedConnection
}

Also upgraded MongoDB Atlas to M20 for connection limit increase.

Fix #3: Redis Caching Layer (3,500 → 6,000 users)

Profiling at 3,500 users showed 60% of API calls were fetching the same data: product listings, user counts, public content. All hitting MongoDB every time.

Added ElastiCache Redis as a caching layer:

// lib/cache.js
const redis = require('ioredis')
const client = new redis(process.env.REDIS_URL)
async function getCached(key, fetchFn, ttlSeconds = 60) {
const cached = await client.get(key)
if (cached) return JSON.parse(cached)
  const data = await fetchFn()
await client.setex(key, ttlSeconds, JSON.stringify(data))
return data
}
// Usage
async function getProducts(category) {
return getCached(
`products:${category}`,
() => db.product.findMany({ where: { category } }),
300 // Cache for 5 minutes
)
}

MongoDB query load dropped by 65%. Median response time went from 600ms to 90ms for cached endpoints.

Fix #4: Auto-Scaling ECS Tasks (6,000 → 8,000 users)

At 6,000 users, traffic was spiky — flat overnight, then surging during business hours. Fixed 3 ECS tasks meant paying for capacity during low traffic and running out during peaks.

Set up auto-scaling:

  • Min tasks: 2 (overnight)
  • Max tasks: 10
  • Scale out at 60% CPU, scale in at 20% CPU
  • 5-minute scale-in cooldown

This cut overnight costs by 40% while handling 2x peaks automatically.

Fix #5: CDN + Static Asset Optimization (8,000 → 10,000 users)

At 8,000 users, the ALB itself became a bottleneck for static assets (images, CSS, JS). Each static file request hit ECS unnecessarily.

Moved all static assets to CloudFront:

Users → CloudFront (static assets) ← S3
→ Vercel (Next.js)
→ ALB → ECS Fargate (API) → Redis → MongoDB

Static file requests dropped off ECS entirely. ALB request count dropped by 70%. ECS CPU utilization dropped from 65% to 38% at peak.

The Final Architecture at 10,000 Users

The Final Architecture at 10,000 Users
The Final Architecture at 10,000 Users

Monthly infrastructure cost at 10,000 users: ~$680/month.

What I’d Do Differently

What I’d Do Differently
What I’d Do Differently
  1. Add Redis from day one. We waited until 3,500 users. It should have been there at 500.
  2. Right-size Fargate from the start. We over-provisioned for months.
  3. Use ISR (Incremental Static Regeneration) more aggressively. Some pages we were server-rendering on every request that didn’t need to be.

Conclusion

Scaling isn’t one big change. It’s a series of targeted fixes, each buying you the next order of magnitude.

The sequence that worked for us:

  1. Horizontal scaling (single → multiple containers)
  2. Connection pooling
  3. Caching layer
  4. Auto-scaling
  5. CDN for static assets

Each fix cost 1–3 days of work. Together they took us from breaking at 500 users to stable at 10,000.

#AWS #Nextjs #Nodejs #Scalability #SoftwareEngineering #ECS #Redis #MongoDB #CloudComputing #Architecture

I’m a Full-Stack Developer specializing in React, Next.js, and Node.js.
👉 Connect with me: https://www.linkedin.com/in/chiragmehta900/

Author card for Chirag Mehta, Full-Stack Developer specializing in React, Next.js and Node.js. Links: github.com/chiragmehta900, linkedin.com/in/chiragmehta900, medium.com/@chiragmehta900.
Chirag Mehta

Originally published on Medium

Clap, comment or follow along there

Read on Medium →