System Design

Magic of Horizontal and Vertical Scaling

Your application is slow. Users are complaining. The server is maxing out CPU and memory. You need more capacity. You have two options: make the server bi...

31 Jan 2024

Magic of Horizontal and Vertical Scaling

Your application is slow. Users are complaining. The server is maxing out CPU and memory. You need more capacity. You have two options: make the server bigger, or add more servers.

That's the entire scaling conversation in one sentence. Everything else is trade-offs.

Horizontal Scaling: Add More Machines

Horizontal scaling means adding more servers to share the workload. Instead of one server handling 10,000 requests, you have ten servers each handling 1,000. A load balancer distributes traffic across them.

When It Works Well

  • Redundancy. If one server dies, the others keep running. No single point of failure.
  • Cost-effective at scale. Ten commodity servers are often cheaper than one server with 10x the resources.
  • Incremental growth. Need 20% more capacity? Add two servers. No downtime, no hardware migration.
  • Independent scaling. In a microservices architecture, you can scale the bottleneck service without scaling everything else.

When It Gets Hard

  • State management. If your server stores session data in memory, adding a second server breaks sessions. You need external state stores (Redis, database-backed sessions) or sticky sessions. Sticky sessions defeat the purpose of load balancing.
  • Data consistency. Multiple servers writing to the same database introduces concurrency issues. Multiple databases mean synchronization challenges.
  • Not all workloads parallelize. A single database query that takes 10 seconds won't run faster on 10 servers. You need to redesign the query, not add machines.
  • Coordination overhead. More servers mean more networking, more monitoring, more deployments, more things that can fail.

Vertical Scaling: Make the Machine Bigger

Vertical scaling means upgrading the existing server — more CPU cores, more RAM, faster SSDs, better network cards. One machine handles everything.

When It Works Well

  • Simplicity. One server. One database. One deployment. No load balancers. No distributed state. No coordination headaches.
  • CPU-intensive workloads. Tasks like video encoding, ML inference, or complex database queries benefit from raw processing power, not more machines.
  • Quick fixes. Traffic doubled overnight? Upgrade to the next instance type. Done in minutes, no architecture changes.

When It Breaks Down

  • Hard ceiling. There's a biggest server you can buy. AWS's largest instance has 448 vCPUs and 24 TB of RAM. If that's not enough, you're stuck.
  • Cost curve. Performance doesn't scale linearly with price. A server with 2x the resources often costs 3-4x more. At the high end, you're paying a premium for diminishing returns.
  • Single point of failure. One machine means one failure point. If it goes down, everything goes down.
  • Downtime for upgrades. Changing hardware usually requires a restart. Horizontal scaling lets you add capacity without downtime.

How I Choose

I don't pick one or the other. I use both, depending on the component.

Application servers — horizontal scaling. Stateless services behind a load balancer. Easy to add capacity. Built-in redundancy.

Databases — vertical scaling first. A bigger database server is simpler than sharding. When vertical scaling runs out (and it will eventually), then shard or add read replicas.

Caches — horizontal scaling. Redis Cluster or Memcached across multiple nodes. Cache invalidation is complex, but scaling a cache horizontally is well-understood.

Background workers — horizontal scaling. Add more workers to process more jobs. Each worker is independent.

The most common mistake I see: teams try to horizontally scale a stateful service without making it stateless first. They end up with sticky sessions, distributed locks, and race conditions. Fix the architecture before you scale it.

The Real Answer

Start simple. One server. Vertical scaling. When you hit the ceiling or need redundancy, go horizontal. But design for horizontal from day one — stateless services, external state stores, no local file dependencies. That way, when you need to scale out, it's a configuration change, not a rewrite.

Keep reading