Your App Is Slow. Now What?

Traffic doubled and your server is struggling. You have two basic options:

  • Vertical scaling (scale up): get a bigger machine: more CPU, RAM, faster disks.
  • Horizontal scaling (scale out): add more machines and spread the load across them.

Both work. They have very different costs, limits and engineering requirements, and real systems usually use both at different layers.


🎯 Quick Answer (30-Second Read)

  • Vertical: simplest. No code changes. But there's a ceiling (the biggest machine available), costs rise steeply near the top, and one machine is a single point of failure.
  • Horizontal: nearly unlimited, better fault tolerance, can scale automatically. Requires stateless app servers, a load balancer, and harder work for databases.
  • Typical pattern: scale app servers horizontally, scale the database vertically first, then add read replicas, then shard.
  • Start simple: a well-tuned single server goes a long way. Scale out when you need resilience or have hit the ceiling.

The Two Approaches

flowchart LR subgraph V["Vertical: scale up"] S1["Server
4 CPU · 16 GB"] -->|"upgrade"| S2["Server
64 CPU · 512 GB"] end subgraph H["Horizontal: scale out"] LB["Load balancer"] --> A1["Server"] LB --> A2["Server"] LB --> A3["Server"] LB --> A4["+ more"] end

Side-by-Side

Vertical (up) Horizontal (out)
How Bigger machine More machines
Code changes Usually none App must be stateless; data must be shared or split
Upper limit Largest available machine Very high
Fault tolerance One machine = single point of failure Survives losing machines
Cost curve Rises steeply for top-end machines Roughly linear
Downtime to scale Often a restart Add servers live
Complexity Low Higher: load balancing, distributed data

What Makes Horizontal Scaling Possible

Key takeaway: to add servers freely, any server must be able to handle any request. That means stateless app servers: no user sessions or uploaded files kept only on one machine's memory or disk.

Move state out of the app servers:

  • Sessions → Redis, the database, or signed cookies/JWTs
  • Uploaded files → object storage (S3, Supabase Storage)
  • Caches → a shared cache like Redis
  • Background jobs → a queue that any worker can consume

Then a load balancer spreads requests, and autoscaling adds or removes servers based on load. (See Load Balancer Explained.)


The Hard Part: Scaling the Database

App servers are easy to clone. Databases hold state, so they scale in stages:

flowchart LR A["1. Bigger DB server
(vertical)"] --> B["2. Read replicas
reads spread out"] B --> C["3. Caching
(Redis) for hot reads"] C --> D["4. Sharding
split data across servers"]
  1. Scale up the database server. Often enough for a long time.
  2. Read replicas: copies that serve reads, while one primary handles writes. Great for read-heavy apps; replicas may lag slightly.
  3. Caching: keep frequently read data in memory.
  4. Sharding: split data across multiple databases (by user ID, region, etc.). Powerful but complex: cross-shard queries and rebalancing get hard. (See Consistent Hashing Explained and Instagram System Design.)

A Real Progression

Stage Setup
Launch One server running app + database
First growth Separate database server; scale both up
Thousands of users 2+ stateless app servers behind a load balancer (also gives redundancy)
Read-heavy growth Read replicas + Redis cache
Massive scale Sharded or distributed database, multiple regions

Many successful products stay at the middle stages for years. Stack Overflow, for example, has famously run on a small number of powerful servers, showing how far vertical scaling plus good engineering can go.


How to Decide

  • Need more capacity quickly, app isn't stateless yet? Scale up.
  • Need high availability (no single point of failure)? Scale out with at least two servers.
  • Traffic is spiky? Horizontal + autoscaling.
  • Database is the bottleneck? Optimise queries and indexes first, then scale up, then replicas and caching, then shard.

Frequently Asked Questions

What is the difference between horizontal and vertical scaling?

Vertical scaling adds resources like CPU and RAM to a single machine. Horizontal scaling adds more machines and distributes the load among them. Vertical is simpler but limited and creates a single point of failure; horizontal scales further and tolerates failures but adds complexity.

Is horizontal scaling always better?

No. It requires stateless application design, load balancing and distributed data strategies, which add complexity. For many applications, a well-optimised single server scaled vertically is cheaper and simpler. Horizontal scaling becomes necessary for high availability or very large workloads.

How do you scale a database horizontally?

Common approaches are read replicas, which copy data to additional servers that handle reads, and sharding, which splits data across multiple databases by a key such as user ID. Distributed SQL and NoSQL databases handle partitioning automatically.


Conclusion

Vertical scaling buys a bigger machine; horizontal scaling adds more machines. Make app servers stateless so they can scale out behind a load balancer, scale the database up first, then add replicas, caching and finally sharding as needed. Choose the simplest option that meets your capacity and availability needs.


Sources

Last verified: 2 November 2026.


Related reads: Load Balancer Explained · Consistent Hashing Explained · CAP Theorem Explained · Why Apps Crash During High Traffic

I write about system design and how real products are built. Follow on LinkedIn or X, or invite me to speak.