Skip to content

BlogEngineering Resources

Vertical vs. Horizontal Scaling: What a Second Server Costs

Vertical vs. Horizontal Scaling: What a Second Server Costs

A second API server can improve availability. It can also, by raising the amount of downstream work the application tier admits, overload your database.

That risk is easy to miss when vertical and horizontal scaling are presented as a maturity path: start with one server, then add more when the system becomes serious. But a larger server and an additional server solve different problems. Vertical scaling increases the capacity of one unit. Horizontal replication changes the topology of the application tier. For a homogeneous request-serving tier, the right move depends on the measured bottleneck, the recovery objective, and whether the benefits justify the distributed-systems costs.

The scope here is vertical resizing versus horizontal replication of a homogeneous API or application request-serving tier in front of shared downstream services. Scaling a database or another stateful tier through replication, partitioning, or sharding introduces ownership, quorum, consistency, migration, and routing questions outside this model. Brokers, caches, and worker or queue-consumer fleets also have different coordination and delivery trade-offs.

If CPU or memory is the measured bottleneck, a larger host has tested headroom, and the application can use the additional resources efficiently, vertical scaling is often the simpler next capacity move. Horizontal replication becomes the appropriate trade when aggregate capacity, recovery, or planned-change requirements justify cross-host routing and readiness across the required failure boundary. State handling and connection budgets need review in either case: they follow process, worker, pool, and replica boundaries rather than host count, and they can become constraints before a second host exists.

A sound architecture proposal names that trade before choosing a design.

A production design can use both

Vertical and horizontal scaling are not opposing destinations. A production system might move from two small instances to two larger instances, then to six larger instances as traffic grows. The team chooses an efficient instance size vertically and replicates that unit horizontally when availability or aggregate capacity requires it.

The useful questions are:

  • What resource is limiting the current system?
  • How efficiently can one instance use more resources?
  • What throughput can one instance sustain at the latency target?
  • What interruption and recovery time can the service tolerate?
  • Which failures must the architecture continue operating through?
  • What new coordination costs appear when the fleet grows?
  • How variable is demand, how long do bursts last, and how uncertain is the forecast?
  • How long do provisioning, replica startup or prewarming, and vertical resizing or replacement take, and what interruption does each cause?
  • What minimum capacity and billing increments apply, and how does capacity scale down after a burst?

"Scale vertically" and "scale horizontally" are incomplete answers until those questions have been addressed.

Vertical scaling is often the right next capacity move

Consider an API running on one 8-vCPU instance. Load tests show CPU saturation while database latency, storage throughput, and network utilization remain within their operating ranges. The application can use additional cores effectively, and a larger instance has enough headroom for the expected demand. Under those conditions, moving to a larger host is a reasonable response.

The deployment still has one serving instance, which is a single point of failure. It does not yet require cross-host request routing, fleet-wide load balancing, or coordination among independently failing application instances.

That does not mean the application has only one process. A larger machine may run more worker processes, threads, containers, or runtime instances, and process-local state may already require affinity or an external store even before the service scales across hosts. The advantage is narrower: the capacity change matches the measured bottleneck without introducing cross-host distribution.

Before resizing, determine whether the change requires downtime or a restart and what tested throughput the next host size provides. If the bottleneck is an unindexed database query, serialization or lock contention around a globally shared resource, or a downstream rate limit, neither a larger API server nor another replica addresses the actual constraint. Process-local serialization or locks may instead be relieved by additional workers or replicas.

Measure the vertical cost curve

Vertical scaling has a hard ceiling. A platform offers finite machine sizes, and applications do not always gain useful throughput in proportion to the resources added. The next host may also have different unit economics.

Suppose a representative load test produces these illustrative results. The cost-per-request figures assume each host runs continuously at its tested sustained throughput for the full billed hour:

8-vCPU host:
  Sustained throughput at latency target : 800 RPS
  Hourly cost                            : $0.40
  Requests per hour                      : 2,880,000
  Host-compute cost per million requests : ~$0.14

16-vCPU host:
  Sustained throughput at latency target : 1,400 RPS
  Hourly cost                            : $0.80
  Requests per hour                      : 5,040,000
  Host-compute cost per million requests : ~$0.16

The larger host increases capacity, but throughput rises by only 75% while cost doubles. It may still be the correct move because it avoids architectural complexity, but it is not automatically the cheaper host-compute unit.

The table is a capacity-normalized application-host compute floor at continuous tested throughput, not a workload cost under variable utilization or a total system cost. Redundancy headroom, routing, downstream services, and operational costs need separate accounting.

Demand shape matters too. Under variable load, a larger host may leave purchased headroom idle, while replica counts can match capacity in finer, reversible increments if provisioning, startup, prewarming, scale-down, and billing behavior permit it. Horizontal redundancy carries its own headroom that cannot be removed without changing the recovery posture. Demand variance, burst duration, forecast uncertainty, and minimum capacity or billing increments can therefore change the comparison even while a larger host has tested vertical headroom. The vertical path should be measured, not followed indefinitely.

Capacity and availability are separate arguments

The cost curve and machine-size ceiling are capacity concerns. They are separate from the strongest reason to add another server: one serving instance is a single point of failure.

An API can have substantial capacity headroom and still fail its availability target. If the only host stops serving traffic, the service remains unavailable until the process recovers, the host restarts, or a replacement takes over. A larger host raises the capacity ceiling; it does not remove that dependency. This can justify horizontal scaling before traffic does.

Two instances may allow one to continue serving while the other is unavailable, but only if their placement separates the host, rack, zone, region, or other boundary implicated by the stated failure and recovery objective. The routing and downstream layers must also support that recovery path, and correlated routing, control-plane, or shared-dependency failures remain in scope. A load balancer can route requests among healthy targets and stop sending new traffic to a target that fails its health checks.

But "two instances" does not by itself guarantee high availability. The design still has to account for:

  • expected failure frequency;
  • detection time and health-check quality;
  • interruption during connection draining and failover;
  • replacement and full recovery time;
  • remaining capacity after a failure;
  • shared database and cache dependencies; and
  • failures in the routing layer itself.

Those factors contribute to error-budget consumption and whether the design meets its recovery objectives. How that consumption is counted also depends on the service's SLI definition, incident blast radius, affected traffic or time, and outage duration. Capacity may permit vertical scaling even when recovery requirements warrant horizontal redundancy.

Planned changes can independently justify another replica. Maintaining service while an instance drains for a rolling deployment or host maintenance requires a ready target, routing support, and enough remaining or temporary surge capacity. A separate target and explicit routing can also isolate a risky canary or overlapping version, even when ordinary demand fits on one host and unplanned-failure recovery would not otherwise require redundancy.

Active-passive is not free redundancy

The second instance does not have to serve active traffic. An active-passive arrangement can reduce some of the concurrency and coordination concerns of active-active deployment, but it shifts risk into failover readiness.

A passive instance may have:

  • stale configuration;
  • cold caches;
  • incomplete replication;
  • expired credentials;
  • insufficient capacity;
  • untested network paths; or
  • a failover mechanism that has not run under real load.

A team may choose active-passive so only one API instance normally serves traffic. That reduces steady-state serving overlap, but it trades overlap for standby decay and failover risk. Deployments and failover can still create a brief period when both instances accept requests. That ordinary concurrent serving must be safe for the request workload, but it does not by itself mean the same logical request will run twice.

Retry safety is a baseline concern even for a single API instance because a client may retry after a timeout or network interruption. Duplicate execution may already be safe when an operation is naturally idempotent. Otherwise, domain uniqueness or transactional constraints, conditional writes, or request-key deduplication at the side-effect boundary can provide the required semantics.

Replay is distinct from split-brain or conflicting execution of work that requires exclusive ownership or ordering. That work may require fencing, leases, leader election, or another proven ownership invariant; idempotency alone does not make arbitrary concurrent effects safe.

A standby that cannot safely take traffic is not redundancy. It is an expensive assumption. Active-passive designs therefore need continuous validation, representative health checks, and regular failover testing. The standby must have current configuration, working dependency access, and enough capacity before routing sends it traffic. A validated hot standby can meet a recovery objective when detection, routing, and readiness complete within the tolerated interruption.

What arrives with server number two

Adding another API instance creates design decisions, although the marginal work depends on the deployment baseline. A platform may already route through an ingress or load balancer, discover instances through an orchestrator, keep request state outside the process, or cap database admission through a connection proxy. Multiprocess deployments may already need local-state and pool controls before a second host exists.

The costs below fall into three categories:

  • topology-specific costs, including cross-host routing and placement across the failure domains in the recovery objective;
  • replica-count costs, including duplicated per-instance pools, caches, background work, and configuration; and
  • general capacity and concurrency costs, including greater admission of downstream work, which can also arise when vertical growth adds workers.

These are not reasons to avoid horizontal scaling. They are costs that belong in the proposal.

Routing becomes part of the system

With one host, the request destination can be direct. With two independently routable hosts, something must choose a healthy target. If the platform already provides ingress, load balancing, or service discovery, the second instance extends that existing routing path rather than introducing it from scratch. The routing layer still needs health checks, timeouts, connection draining, observability, capacity awareness, and its own failure plan. A managed load balancer reduces the infrastructure the team operates, but it does not remove the design work.

A shallow health check may keep sending traffic to an instance that can answer /health but cannot reach a required dependency. An overly strict check may remove instances during a temporary downstream slowdown and concentrate traffic on the remaining fleet.

Capacity planning also changes. If two instances are both required to handle normal peak traffic, losing one leaves insufficient capacity. If each instance can handle peak traffic alone, the service has clearer redundancy but pays for significant idle headroom. The right answer depends on the recovery objective, expected failure duration, and cost target.

Session state needs an explicit home

Suppose the original API stores login or workflow state in process memory. After another instance is added, request one may reach instance A and request two may reach instance B. Instance B does not have A's local state. The same issue can predate horizontal replication when one host runs multiple processes; replica two extends the boundary across independently failing hosts.

There are several valid responses:

  • move session state to a shared store;
  • encode appropriate state in a signed client-side token;
  • use routing affinity; or
  • redesign the workflow so the request carries enough information to resume elsewhere.

The choice depends on state size, revocation requirements, consistency needs, security boundaries, and acceptable failure behavior.

Sticky sessions are a decision, not a default. They can preserve local-state behavior by binding a session to one target, but they also couple that session to the target's health and may create uneven load. When the target fails, routing the next request somewhere else does not recover state that existed only in the failed process.

In this context, stateless should mean:

Any healthy instance can process the request using data available outside that instance.

It does not mean the system has no state.

Downstream capacity does not scale automatically

Two API instances may increase available application concurrency, but the database, cache, message broker, and third-party dependencies remain unchanged unless the design scales them separately. A larger host that runs additional workers can create the same downstream pressure. Greater admission of downstream work is therefore a capacity and concurrency cost, not a uniquely horizontal one.

Adding a replica under unchanged offered load does not inherently increase database query rate. The risk appears when previously queued demand is admitted, admission or retry concurrency rises, replica-local background work is multiplied, or another per-instance behavior increases downstream work. Replication uniquely adds another independently routable and independently failing instance, and it can duplicate genuinely per-instance pools, caches, background work, and configuration. Under those conditions, an apparent capacity improvement can create a failure chain:

  1. More application workers accept concurrent requests.
  2. Those requests issue more database queries.
  3. Database latency rises.
  4. Requests hold application resources longer.
  5. Queues and timeouts begin to grow.
  6. Additional application replicas increase pressure on the component already limiting throughput.

The capacity plan therefore needs a downstream budget whether growth is vertical or horizontal. Estimate the concurrency the application tier can generate, identify the limiting dependency, and reserve capacity for background jobs, migrations, monitoring, administrative access, and recovery traffic.

The fleet-wide connection budget trap

Return to the 8-vCPU API instance. Assume the deployment runs eight worker processes and each process owns a database pool configured for up to 20 simultaneous connections.

For one application instance:

1 instance × 8 workers × 20 connections
  = 160 configured application connections

Add a second identical instance:

2 instances × 8 workers × 20 connections
  = 320 configured application connections

A vertical resize can produce the same configured envelope. If a larger host runs 16 workers with the same 20-connection maximum, 1 instance × 16 workers × 20 connections also equals 320 configured application connections.

Now assume the database permits 250 total connections, and 30 must remain available for migrations, background jobs, monitoring, and administrative recovery. The API tier's connection budget is therefore 220:

250 total database connections
- 30 reserved connections

= 220 available to the API tier

320 configured application connections
- 220 available connections

= 100 connections over budget

In either case, the API tier has gained compute capacity while acquiring a configuration that can exceed the database connection budget by 100. The connection-budget hazard follows aggregate worker counts, pool configuration, and admitted concurrency regardless of whether those workers are spread across hosts. Horizontal replication changes that envelope only where configuration or behavior is genuinely per instance.

That does not mean the second host immediately opens 320 connections. Many pools open connections lazily, maintain smaller idle sets, or create overflow connections only under pressure. The calculation defines the fleet's risk envelope: what can happen when every worker becomes busy at once.

The database's connection limit is also only a hard admission ceiling, not necessarily the database's safe operating concurrency. A database configured to accept 250 connections may become CPU-, memory-, I/O-, or lock-bound with far fewer than 250 active queries, and many accepted connections may sit idle without performing useful work.

The real requirement is:

Representative database work must remain within tested latency and resource limits, admitted backend connections must remain within the database connection budget, and active query concurrency must remain within its tested safe range.

Configured application pool maxima remain a risk envelope, but they need not equal the backend budget when a proxy or admission-control layer reliably caps admitted connections. The resulting application-side waiting or refusal must still meet the service objectives.

Possible responses include:

  • reducing per-worker pool sizes;
  • running fewer workers;
  • introducing a connection proxy;
  • applying application-level concurrency limits;
  • increasing database capacity;
  • optimizing the query workload; or
  • separating background and request-serving budgets.

The correct choice depends on observed query latency, transaction duration, active concurrency, and workload shape. A pool should be large enough to keep useful database work moving, but small enough to bound fleet-wide pressure when every application instance is busy.

Make the capacity and topology decisions separately

Choose an instance size from the measured bottleneck, tested per-instance throughput at the required latency and resource thresholds, available headroom, and application-host unit economics. Vertical scaling has a finite ceiling and cannot provide unbounded headroom, but it may remain suitable indefinitely when demand stays below that ceiling. The same sizing decision can apply to one host or every member of a redundant fleet.

For this request-serving tier, choose the replica topology from aggregate capacity, recovery requirements, planned-change requirements, and demand shape. Planned-change requirements include the ready target, routing, and surge capacity needed to drain an instance during deployment or maintenance or isolate version overlap. If a vertical resize preserves a single-host topology, evaluate that topology against the availability analysis above. Vertically resizing members of an already redundant fleet does not create the same dependency.

Compare forecast uncertainty and burst duration with provisioning, replica startup or prewarming, and scale-down time, and compare minimum capacity and billing increments with the interruption required for a vertical resize or replacement. Reversible replica counts can make horizontal replication preferable even when a larger host has tested headroom, but only if capacity can arrive before it is needed and can be removed under the relevant billing model.

Before adding replicas, verify the routing and failover path, define where state lives, preserve capacity after a failure, and budget downstream throughput and admitted connections. The resulting architecture may combine larger instances with horizontal redundancy, provided each change solves its own constraint without exceeding the capacity of shared dependencies.

Share this post