In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.
By Matija Žiberna — Full-stack developer. I build with Next.js, Payload CMS, and TypeScript, mostly deployed on VPSes for clients ranging from small Slovenian businesses to larger international companies. Last updated: June 2026.
For years I deployed Next.js the same way: a VPS, Docker, Postgres, a reverse proxy, and the app itself. Everything worked. I didn't think much about what was actually happening under the hood, and I didn't need to.
Then I started getting questions from clients — and asking them myself. What happens when traffic grows? How does Next.js actually scale? Is it production-ready? Can a self-hosted server genuinely serve a real business, or does it start cracking once load increases?
This article is what I found after digging into those questions through real deployments. The short answer: Next.js is more capable than most developers give it credit for, and the scaling bottlenecks almost never live where you'd expect.
What Actually Runs When You Deploy Next.js
The first misconception I had was that Next.js sat on top of Express, borrowing its HTTP server and just adding a rendering layer. That's not how it works.
In production, Next.js runs as a standalone Node.js server. When you build with output: 'standalone' and deploy the result, you're running:
bash
node server.js
That process handles Server Components, SSR, Route Handlers, API endpoints, image optimization, caching, and Incremental Static Regeneration. It's a real backend process. Framing it as "just a frontend framework" doesn't fit what it actually does.
Understanding that changes how you think about scaling. You're not trying to scale a thin rendering layer sitting in front of something else. You're scaling a Node.js server that has real responsibilities.
How Much Load Can a Single Server Handle?
The better question is: how much dynamic work actually reaches the Node process?
A surprisingly large share of traffic can bypass it entirely when the setup is right:
Static pages served from a CDN
Cached responses returned before hitting the Node process
ISR pages regenerated in the background and served statically
Image responses cached after the first optimization pass
Assets delivered directly from the edge
When those levers are used correctly, the Node server handles a fraction of total requests. For most applications at most traffic levels, the Node process isn't where pressure accumulates.
The actual bottlenecks tend to be database performance, slow queries, missing indexes, expensive external API calls, and poor caching strategy. A modern VPS handles significantly more traffic than developers usually assume. The question worth asking isn't "Is Next.js fast enough?" — it's "How much unnecessary work am I routing through it?"
How Horizontal Scaling Works for Next.js
When you do need to scale beyond a single server, the answer is horizontal scaling: multiple identical instances behind a load balancer, rather than a larger single machine.
code
Cloudflare
↓
Load Balancer
↓
Next.js Instance A
Next.js Instance B
Next.js Instance C
↓
Postgres
Each instance runs the same code with the same environment variables. The load balancer distributes incoming requests across them. The key constraint this creates is statelessness — no request-specific state can live on the application server itself. Sessions, cache, file uploads — all of that must live outside the process, in Redis, a shared database, or object storage.
This is also what makes horizontal scaling practical. Application servers become disposable. If one dies, the others continue serving traffic. Deployments become a matter of replacing instances one at a time with no downtime.
Shared Infrastructure Across Instances
Multiple Next.js servers connecting to the same database and the same Redis instance is normal and expected. The application servers are ephemeral; the data layer is the source of truth.
A typical environment configuration shared across all instances:
Every instance reads the same config. Any instance can handle any request. That uniformity is what makes the load balancer's job straightforward.
What the Load Balancer Actually Does
The load balancer is the entry point for all traffic. Its responsibilities go beyond just routing requests:
Distributing load across healthy instances
Running health checks and removing unhealthy servers automatically
Handling SSL termination so the app servers work over plain HTTP internally
Providing a single external endpoint regardless of how many instances are behind it
Common options for self-hosted setups:
Tool
Good for
Nginx
Simple, battle-tested, wide support
Traefik
Docker-native, automatic config via labels
HAProxy
High-performance, fine-grained control
Cloudflare Load Balancer
Managed, works well with Cloudflare CDN
For VPS-based deployments I usually reach for Traefik. It integrates cleanly with Docker Compose, picks up new containers automatically via labels, and handles Let's Encrypt certificates without manual configuration.
Where Scaling Actually Gets Difficult
The web servers are the easy part. Once you have multiple stateless Next.js instances behind a load balancer, that layer stays simple. The complexity accumulates elsewhere:
Database scaling is where most teams eventually hit a ceiling. A single Postgres primary handles a lot, but read-heavy workloads benefit from read replicas. The standard pattern:
Application logic routes writes to the primary and reads to replicas. This distributes query load and gives the primary room to breathe.
Cache invalidation becomes harder across multiple instances. Anything cached in memory on instance A isn't visible to instance B. Shared Redis solves this, but it introduces a new dependency to manage.
File storage should move to object storage (S3 or compatible) from the beginning. Local disk storage doesn't work across multiple servers.
Background jobs need a shared queue so they don't run in duplicate across all instances.
Deployments require a strategy — rolling updates, zero-downtime deploys, and health check endpoints become important once you're serving real traffic.
Infrastructure Tiers for Self-Hosted Next.js
There are a few natural levels depending on where the project sits.
Simple, reliable, and easy to reason about. For the majority of client work I run at buildwithmatija.com and through WHCP, this is the right level. A well-tuned Docker Compose setup running 2–3 Next.js replicas with Redis, Postgres, and Cloudflare in front of it can support a serious business without Kubernetes complexity.
Kubernetes — When You Need Orchestration
Kubernetes manages replicas, rolling deployments, autoscaling, secrets, storage, and health checks. If a node dies, it reschedules the affected containers. If traffic spikes, it can spin up additional replicas automatically. The tradeoff is real operational complexity. Kubernetes is the right answer at scale, but it's expensive to manage without dedicated infrastructure resources.
Managed Cloud — Delegated Complexity
AWS, GCP, and similar providers offer managed versions of each infrastructure component: RDS instead of self-managed Postgres, ALB instead of self-managed Nginx, EKS instead of self-managed Kubernetes. The operational complexity doesn't disappear — it shifts from your team to the provider.
What About Docker Swarm?
Docker Swarm still works. It's genuinely simpler than Kubernetes and can orchestrate multi-server deployments with less overhead. For teams that need something between Docker Compose and Kubernetes, it's a reasonable option technically.
In practice, most tooling, tutorials, and hosting solutions have consolidated around Kubernetes. For any project that will eventually need orchestration, going directly from Docker Compose to Kubernetes skips a migration step. Swarm is a viable detour, but most teams bypass it.
FAQ
Is Next.js production-ready for self-hosted deployments?
Yes. A standalone Next.js build runs as a proper Node.js server and handles SSR, API routes, image optimization, and caching. It doesn't require Vercel or any managed platform to work reliably.
Do I need Kubernetes to scale Next.js?
For most projects, no. A Docker Compose setup with 2–3 replicas behind Traefik and a properly configured Postgres instance handles significant traffic. Kubernetes becomes relevant when you need automated scaling, multi-node orchestration, or large team deployments.
Can multiple Next.js instances share one database?
Yes, and this is the standard pattern. Each instance connects to the same Postgres primary (and optionally read replicas). The instances themselves stay stateless; the database holds all persistent state.
What's the most common scaling mistake?
Treating the Node process as the bottleneck before confirming it actually is. Most Next.js servers under pressure are struggling with slow database queries or missing caches, not with Node.js throughput. Fixing queries and adding caching layers typically resolves the issue before any horizontal scaling is needed.
Where should session and cache data live in a multi-instance setup?
In Redis, shared across all instances. In-memory caches tied to a single process break as soon as you have more than one server. Redis gives you a shared, fast store that every instance reads and writes consistently.
Conclusion
After deploying Next.js across a range of client projects — from single-VPS setups to multi-instance setups with load balancers and read replicas — the mental model that helped most was this: the Node server works best when it does less.
A well-configured self-hosted setup with aggressive caching, a CDN in front, static generation where possible, and queries that don't block unnecessarily will outperform an over-engineered multi-server setup with none of those things in place. Horizontal scaling is a real option when you need it, and the stateless architecture of Next.js makes it straightforward to implement. The infrastructure patterns covered here — Docker Compose with replicas, Traefik, shared Postgres, Redis — aren't theoretical. They're what runs actual client projects today.
If any of this raised questions about your own setup, drop them in the comments. And subscribe if you want more practical guides on deploying Next.js and Payload in production.