open atlas
↑ Back to track
System Design Foundations SD · 01 · 02

Vertical vs horizontal scaling

Scaling up means a bigger machine; scaling out means more machines. Up is simple but hits a hardware ceiling and stays a SPOF; out is elastic and fault-tolerant but forces statelessness and pays a coordination tax the Universal Scalability Law makes concrete.

SD Middle ◷ 18 min
Level
FoundationsJuniorMiddleSenior

A startup’s API ran on one beefy database server. Traffic grew, so they did the obvious thing: a bigger instance. Then a bigger one. Then the biggest their cloud offered — 128 vCPUs, costing more per month than two engineers. When that saturated, there was no “next size up,” and the migration to a sharded, multi-node design that should have started two years earlier now had to happen during an outage. The lesson wasn’t “vertical scaling is bad.” It was that they’d chosen the path with a ceiling without ever asking where the ceiling was.

Two directions

When you hit a capacity wall, you have exactly two moves. Understanding what each one costs — and where each one fails — is what separates a rushed fix from a decision you won’t regret in twelve months.

Vertical scaling (scale up) — give one machine more resources: more CPU, RAM, faster disks, a bigger instance type. The application barely changes; you reboot onto bigger hardware and capacity grows. Horizontal scaling (scale out) — add more machines and spread the work across them behind a load balancer.

The appeal of up is that it is almost free in engineering effort: no distributed-systems problems, no consistency questions, your code doesn’t know it’s on a bigger box. The appeal of out is that it has no inherent ceiling and survives machine death — lose one node of fifty and you’ve lost 2% of capacity, not your service.

Real systems use both: scale up the database tier until a single primary is uncomfortable, scale out the stateless app tier freely. The senior question is never “up or out?” in the abstract — it’s “which resource is the bottleneck, does that resource have a ceiling I’ll hit, and what does each path cost me in failure modes?”

The two ceilings of scaling up

Vertical scaling fails for two independent reasons, and you hit whichever comes first:

  1. The hardware ceiling. There is a largest instance. When you’re on it, you are done — there is no bigger box, and the lead time to re-architect is now measured in quarters.
  2. The cost curve. Top-end hardware is priced superlinearly: the biggest instance often costs far more than 2× the one half its size, because you’re paying for the rare high-core-count silicon. Two medium machines frequently beat one large machine on both price and resilience.

And underneath both: a single vertically-scaled machine is a single point of failure. It reboots, its disk dies, its AZ goes down — and you have a full outage, because there is nowhere for the load to go.

Why this works

Why does scaling out “force” statelessness? Because if request 1 lands on node A and writes session state into A’s memory, request 2 from the same user might land on node B — which has never heard of them. The fix that juniors reach for is sticky sessions (pin a user to a node), but that re-introduces the single point of failure per user and breaks rebalancing: lose node A and every user pinned to it loses their session. The durable fix is to push state out of the app tier entirely — into a shared database, a distributed cache (Redis), or the client (a signed token). Then any node can serve any request, and the app tier becomes a herd of identical, disposable cattle you can add and kill at will.

The coordination tax: the Universal Scalability Law

The dangerous myth of horizontal scaling is that throughput grows linearly with nodes — double the nodes, double the capacity. It does not. Neil Gunther’s Universal Scalability Law (USL) models the real curve with two penalties:

            N
C(N) = ─────────────────────────
       1 + α(N−1) + βN(N−1)

N = number of nodes
α = contention   (serialized work — Amdahl's law: a shared lock, a single coordinator)
β = coherency    (cross-node coordination — keeping caches/replicas consistent)

With α alone (contention), throughput approaches a ceiling — Amdahl’s law: a 5% serial fraction caps you at 20× no matter how many nodes you add. But β (coherency) is worse: it makes the curve retrograde — past some peak, adding nodes reduces total throughput, because every new node adds more cross-talk than it contributes work. This is why a 50-node cluster can be slower than a 30-node one. Scaling out is not free; you are buying capacity with a coordination tax, and the tax can eventually exceed the capacity.

Where the difficulty actually moves

Scaling out the stateless tier (web/API servers) is close to a solved problem: put them behind a load balancer, autoscale on CPU, done. The difficulty doesn’t vanish — it relocates to the stateful tier. Your database, your queues, your caches still hold state that must stay correct across nodes, and that’s where replication, sharding, and the CAP tradeoffs live (the next unit on data distribution). So “we’ll just scale horizontally” is only easy for the part that holds no state; the moment you scale out the data layer, you’ve signed up for distributed-systems problems.

Common mistake

A classic and expensive mistake: scaling out a service that looks stateless but isn’t. In-memory caches that each node populates independently, local file uploads written to the node’s disk, rate-limit counters kept per-process, background cron jobs that now run on every node at once. Each works fine on one machine and breaks subtly on many — duplicated emails, inconsistent counts, files that exist on only one node. Before you add the second node, audit every piece of state the process holds and decide where each one really lives.

Quiz

A stateless API tier scales out beautifully, but you notice that past ~40 nodes, total throughput stops rising and then slightly falls. By the Universal Scalability Law, which term is dominating?

Quiz

Your team wants to scale out an API server that stores user sessions in local process memory. What is the minimal correct change before adding a second node?

Complete the analogy

To scale the app tier horizontally, each node must be _______ — holding no per-request state locally — so the load balancer can route any request to any node and a dying node loses nothing irreplaceable.

Recall before you leave
  1. 01
    Give the two ceilings of vertical scaling plus its structural weakness.
  2. 02
    Why does horizontal scaling require statelessness, and what is the durable fix for a stateful app tier?
  3. 03
    What does the Universal Scalability Law add beyond Amdahl's law?
Recap

Vertical scaling (a bigger machine) costs almost no engineering effort but hits a hardware ceiling, a superlinear cost curve, and remains a single point of failure. Horizontal scaling (more machines) has no inherent ceiling and survives node death — but only if the work is stateless, which means externalizing session/cache/file state to a shared store or the client (sticky sessions are a band-aid that re-creates per-user SPOFs). Scaling out is not free either: the Universal Scalability Law prices it with α (contention → Amdahl ceiling) and β (coherency → a retrograde curve where extra nodes can lower throughput). The stateless app tier scales out easily; the real difficulty relocates to the stateful tier — the database and caches — which is exactly what the next unit on data distribution tackles. Now when you see a team reaching for a bigger instance, ask first: is this resource stateless? Is there a ceiling we’ll hit in a year? Those two questions are the whole decision.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 7 done
Connected lessons

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.