Horizontal vs Vertical Scaling #

When a web application or API system we’ve built starts experiencing massive user growth, the infrastructure underneath automatically faces increasingly heavy workloads. System responses slow down, transaction queues get delayed, and servers risk crashing from exhausted compute resources. In the world of infrastructure engineering, there are two fundamental paradigms we can choose from to increase system capacity for handling load surges: Vertical Scaling (scaling up / increasing a single node’s capacity) and Horizontal Scaling (scaling out / adding more nodes). Both approaches have their place in system architecture, but in the modern cloud-native era, horizontal scaling is the default pattern we must master because it’s the only mechanism that enables maximum system elasticity and resilience.

Basic Concepts: Adding Power vs Adding Manpower #

To understand the conceptual difference between both scaling models, let’s use a simple analogy of a company’s logistics delivery system:

  • Vertical Scaling (Scale Up / Scale Down): We have one delivery courier using a pedal bicycle. When order volume increases, we scale up that courier by buying them a motorcycle, then upgrading again to a box truck, and finally buying them a giant semi-truck. We increase the carrying capacity of the same single courier.
  • Horizontal Scaling (Scale Out / Scale In): Instead of buying a giant semi-truck for one courier, we hire 10 new couriers, each on a motorcycle, working in parallel to split the delivery load evenly.
flowchart TD
    subgraph ScaleUp["Vertical Scaling (Scale Up)"]
        direction TB
        VM_Small["Small VM<br>2 vCPU, 4GB RAM"] -->|"Change Instance Type (Reboot)"| VM_Huge["Giant VM<br>32 vCPU, 64GB RAM"]
    end
    
    subgraph ScaleOut["Horizontal Scaling (Scale Out)"]
        direction TB
        VM_1["VM 1<br>2 vCPU"] -->|"Scale Out (Add Nodes)"| Nodes["Parallel Node Cluster"]
        subgraph Nodes
            VM_A["VM A<br>2 vCPU"]
            VM_B["VM B<br>2 vCPU"]
            VM_C["VM C<br>2 vCPU"]
        end
    end

Deep Analysis: The Physical and Economic Limits of Vertical Scaling #

Vertical Scaling (Scale Up) is done by allocating larger vCPU, RAM, and storage specifications on the same Virtual Machine instance. Although it sounds very practical because it doesn’t change our application architecture, this model has serious limitations that become unavoidable as the system grows rapidly:

1. Physical Hardware Ceiling #

The laws of physics limit how large CPU chips and RAM modules can be installed on a single physical motherboard. There’s a saturation point where we can no longer buy larger VM instances due to semiconductor manufacturing constraints. When our server reaches the highest specification available from the cloud provider, we have no more room to grow except switching to horizontal scaling.

2. Server Reboot Requirement (Downtime) #

On most cloud platforms, resizing a Virtual Machine (for example, from a t3.micro to an m5.large) requires shutting down the instance first so the hypervisor can reconfigure the new virtual hardware allocation, then starting it again. This causes several minutes of service interruption (downtime) — highly undesirable for production applications with high SLAs.

3. Non-Linear Cost Curve (Premium Cost Penalty) #

Cloud VM rental prices rise exponentially past a certain size threshold. For example, renting one giant server with 128 vCPUs and 512GB RAM is often far more expensive than renting 16 small servers with 8 vCPUs and 32GB RAM simultaneously, even though the total compute capacity is the same. We pay a premium price for that hardware density.

4. Single Point of Failure (SPOF) #

Even if we have a very expensive, very fast giant server, if the operating system hits a kernel panic, the host hypervisor suffers physical damage, or a DDoS attack cripples that server’s ports, our entire application dies because no backup server automatically takes over.


Horizontal Scaling: The Pillar of Cloud-Native Scaling #

Horizontal Scaling (Scale Out) is done by adding identical server instances under the coordination of a single load-balancing gateway (Load Balancer). Horizontal scaling offers extraordinary resilience advantages:

  • No Capacity Limit (Infinite Scaling): We can add dozens, hundreds, even thousands of small server instances in parallel to serve world-scale traffic. Our capacity limit is purely financial budget, not hardware technology.
  • Built-in High Availability (Resilience): If one server in our horizontal cluster dies from system failure, the Load Balancer immediately detects the damage and stops sending traffic there. Other healthy servers take over the request load with no user-perceived downtime.
  • Elastic Cost Efficiency: We can configure auto-scaling so server counts shrink drastically at night (for example, from 20 servers to 2) to save monthly rental costs.
  • Zero-Downtime Deployment: Horizontal scaling lets us update application versions gradually (rolling update) by releasing new-version servers in parallel before shutting down old-version servers.

The Load Balancer’s Role in Horizontal Scaling #

Horizontal scaling cannot function without a Load Balancer in front of our server cluster. The load balancer acts as a network traffic police officer that receives all incoming requests from the public internet and distributes them fairly to healthy backend servers.

flowchart TD
    User["User"] --> LB["Load Balancer"]
    LB -->|"traffic forwarded"| A["Instance A (healthy)"]
    LB -->|"traffic forwarded"| B["Instance B (healthy)"]
    LB -->|"traffic forwarded"| C["Instance C (healthy)"]
    LB -. "traffic NOT forwarded" .-> D["Instance D (unhealthy)"]

Load Balancer Traffic Distribution Algorithms #

To distribute load fairly, the Load Balancer uses the following algorithms:

  1. Round Robin: Distributes requests sequentially and fairly, one by one, to each backend server.
  2. Least Connections: Routes new requests to the server currently handling the fewest active connections. Great for requests with varying processing durations.
  3. IP Hash: Locks a client IP address to a specific backend server so users always land on the same server (useful if the application is still stateful/sticky-session based).

Load Balancing Comparison: Layer 4 vs Layer 7 #

In cloud network architecture design, we must understand the difference between Layer 4 and Layer 7 load balancers:

CriteriaLayer 4 Load Balancing (L4)Layer 7 Load Balancing (L7)
ProtocolWorks at the Transport level (TCP, UDP).Works at the Application level (HTTP, HTTPS, WebSockets).
SpeedVery Fast (Only routes IP packets without inspecting data content).Slightly Slower (Must decrypt SSL and inspect HTTP payloads).
Routing IntelligenceCan’t see URLs, paths, headers, cookies, or request body contents.Very Smart (Can route based on /api/v1/users or domain).
SSL TerminationUsually passed straight through to backend servers.Handles SSL decryption at the Load Balancer (reduces backend CPU load).
Service ExamplesAWS Network Load Balancer (NLB), HAProxy (TCP mode).AWS Application Load Balancer (ALB), Nginx (HTTP mode), Traefik.

Automation with Auto-Scaling Groups #

The biggest advantage of horizontal scaling in the cloud is its ability to be configured automatically using Auto-Scaling Groups (ASGs). We no longer need to ask a Sysadmin to monitor CPU graphs at midnight to create new VMs.

We simply define policy rules (scaling policies) like this:

  • Scale-Out Target: “If the cluster’s average CPU utilization exceeds 70% for 3 monitoring periods, add 2 new server instances.”
  • Scale-In Target: “If the cluster’s average CPU utilization stays below 30% for 10 consecutive minutes, gracefully terminate 1 server instance.”

The auto-scaler continuously evaluates these metrics in real-time and directly interacts with the cloud provider’s API to dynamically trigger VM creation or destruction.


Scaling Strategies at the Database Level #

Although stateless application servers are very easy to scale out horizontally, the relational database layer is the hardest component to scale horizontally due to data write consistency issues.

Therefore, modern database architectures adopt a combination of both scaling methods:

1. Database Read Replicas (Horizontal Read) #

For applications dominated by data read operations (like news portals or e-commerce), we create one primary database server (Primary) to handle write operations (INSERT/UPDATE), and automatically duplicate that data to several backup database servers (Read Replicas). Application servers route all SELECT queries to the Read Replicas to split the workload horizontally.

2. Database Sharding (Horizontal Write) #

For applications with massive data write traffic, we’re forced to split database table rows across several different physical database servers based on a sharding key (for example, separating customer transaction data by their region of residence).

Here’s an example of declarative Terraform configuration to deploy a horizontal compute layer system (Auto Scaling Group) behind an Application Load Balancer with Multi-AZ resilience:

# ✓ CORRECT: Use Auto Scaling Groups & Application Load Balancer for Horizontal Compute Scaling

# Application Load Balancer (ALB) definition
resource "aws_lb" "app_alb" {
  name               = "production-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.alb_sg.id]
  subnets            = [aws_subnet.public_az1.id, aws_subnet.public_az2.id]
}

# Target Group where Auto Scaling dynamically registers VMs
resource "aws_lb_target_group" "app_tg" {
  name     = "app-target-group"
  port     = 80
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id

  # Periodic Health Check configuration
  health_check {
    path                = "/healthz"
    protocol            = "HTTP"
    matcher             = "200"
    interval            = 15
    timeout             = 3
    healthy_threshold   = 2
    unhealthy_threshold = 3
  }
}

# Launch Template defining the VM instance blueprint created by the ASG
resource "aws_launch_template" "app_lt" {
  name_prefix   = "app-v1-template-"
  image_id      = "ami-0c55b159cbfafe1f0" # Ubuntu Golden Image
  instance_type = "t3.medium"              # Balanced instance size (cost-effective)

  network_interfaces {
    associate_public_ip_address = false # VM placed in private subnet for security
    security_groups             = [aws_security_group.app_sg.id]
  }

  user_data = base64encode(<<-EOF
              #!/bin/bash
              echo "Starting App Service..."
              # Application startup script goes here
              EOF
  )
}

# Auto Scaling Group managing dynamic instance count
resource "aws_autoscaling_group" "app_asg" {
  desired_capacity    = 2  # Initial target capacity
  max_size            = 10 # Maximum server limit for budget safety
  min_size            = 2  # Guarantees minimum redundancy of 2 servers in 2 different AZs
  vpc_zone_identifier = [aws_subnet.private_az1.id, aws_subnet.private_az2.id]

  target_group_arns = [aws_lb_target_group.app_tg.arn]

  launch_template {
    id      = aws_launch_template.app_lt.id
    version = "$Latest"
  }

  # Protection policy on shutdown (Connection Draining)
  suspended_processes = []
  
  # Monitoring integration
  metrics_granularity = "1Minute"
  enabled_metrics     = ["GroupMinSize", "GroupMaxSize", "GroupDesiredCapacity", "GroupInServiceInstances"]
}

Operational Hurdles and State Coordination in Horizontal Scaling #

Although horizontal scaling offers almost unlimited scalability, this approach brings new complexity known as distributed coordination. When we run applications across dozens of parallel servers, we must solve the following problems:

1. Data Consistency (CAP Theorem) #

Based on the CAP Theorem, distributed systems can only guarantee two of three properties: Consistency, Availability, and Partition Tolerance. When we scale horizontally (guaranteeing availability and partition tolerance), we must accept the consequence of Eventual Consistency. For example, data written to the primary database takes milliseconds to seconds to replicate to all read replicas. Users might read slightly stale data if reading from a replica with lag.

2. File Upload Handling #

In a single-server (vertical) architecture, user-uploaded files are stored on the server’s local hard drive. In horizontal architecture, a file uploaded to Server-A can’t be accessed if the next request is served by Server-B. Therefore, storing files on VM local disks is strictly forbidden. All files must be moved to an agnostic centralized object storage service like Amazon S3 or Google Cloud Storage.

3. Scheduled Task Synchronization (Scheduled Tasks / Cron Jobs) #

If our application has scheduled background tasks (like sending daily report emails at midnight) and that code runs on 10 horizontal servers, the task executes 10 times redundantly. The solution: separate these scheduled worker processes to run only on a dedicated single instance, or use distributed locking mechanisms with Redis (SETNX) to guarantee only one server successfully executes the task.


Summary #

  • Vertical Scaling increases a single server’s capacity (vCPU/RAM), while Horizontal Scaling adds server instance counts in parallel.
  • Vertical Scaling has a hard physical ceiling, requires reboots (downtime) on upgrades, and creates a Single Point of Failure (SPOF).
  • Horizontal Scaling provides high resilience, automatic auto-healing, and elastic cost efficiency using Auto Scaling Groups.
  • A Load Balancer is the mandatory gateway for horizontal scaling; Layer 7 ALBs offer smart URL-based routing while Layer 4 NLBs focus on throughput speed.
  • Databases scale horizontally using Read Replicas to split data read loads, or Sharding to distribute write transactions.
  • Apply Auto Scaling Groups in the cloud with a minimum configuration of 2 nodes spread across different Availability Zones to guarantee High Availability.
  • Move uploaded media to external Object Storage and use distributed locking to prevent duplicate cron jobs on horizontal systems.

← Previous: Stateless vs Stateful   Next: Immutable Infrastructure →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact