Fun with Learning Technology
LearnCoursesQuestionsTracksToolsNewsExplorePractice
Fun with Learning Technology

A new problem, explained clearly, every day.

Subscribe
Learn
  • Lessons
  • Topics
  • News
  • Tools
  • Courses
  • Career tracks
  • Everything
Site
  • About
  • Contact
  • Support
  • Privacy
  • Terms
Get the daily one

One email per new problem. No spam.

Request a tutorial

Requests shape what gets made next.

© 2026 Fun with Learning TechnologyRSS
Home›Courses›System Design›Load Balancing

Fundamentals

Load Balancing

Load balancing is the systematic distribution of incoming network traffic across multiple backend servers to ensure no single node becomes a bottleneck. It is essential for achieving high availability and horizontal scalability, preventing service downtime during traffic spikes. You reach for a load balancer as soon as a single server instance can no longer handle the total request load or when you require fault tolerance to maintain service uptime.

The Core Concept: Traffic Distribution

At its simplest, a load balancer acts as an intelligent intermediary that sits in front of a group of servers. The primary goal is to ensure that compute resources are utilized efficiently by preventing any single server from being overwhelmed by requests while others sit idle. By acting as a reverse proxy, the load balancer receives the initial request from the client and forwards it to one of the available backend nodes based on a predefined algorithm. This separation of the public-facing entry point from the internal processing logic is crucial for reliability. If a backend server is overloaded or performing poorly, the load balancer can redirect traffic to healthier nodes. This mechanism enables the system to handle traffic fluctuations without requiring the client to know the internal topology of the server cluster, effectively decoupling the service consumption from the underlying infrastructure complexity.

# Simple Round Robin distribution logic
servers = ['server1', 'server2', 'server3']
request_count = 0

def get_next_server():
    global request_count
    # Pick server based on index modulo array size
    server = servers[request_count % len(servers)]
    request_count += 1
    return server

# Simulating distribution
for _ in range(5): print(get_next_server())

Algorithm Selection: Static vs Dynamic

The intelligence of a load balancer lies in its selection strategy. Static algorithms like Round Robin or Weighted Round Robin make decisions based on fixed configurations without considering the current state of the backend servers. While computationally efficient, these approaches often fail in heterogeneous environments where some servers might have more CPU or RAM than others. Dynamic algorithms, conversely, account for real-time metrics. Least Connections, for example, tracks the number of active sessions on each node, sending new traffic to the server with the lowest current load. This is superior for long-lived connections, such as web sockets or database streaming, where a simple round-robin might inadvertently send a new, intensive request to a server already struggling with complex background tasks. Understanding the relationship between request duration and server state allows architects to choose an algorithm that maximizes resource utilization and minimizes latency for the end user.

# Dynamic Least Connections tracking
server_connections = {'s1': 0, 's2': 5, 's3': 2}

def get_least_loaded():
    # Find the key with the minimum value
    return min(server_connections, key=server_connections.get)

# Select the optimal node based on real-time load
target = get_least_loaded()
print(f"Routing to: {target}")

Ensuring Health and Reliability

A load balancer is only useful if it knows which servers are actually capable of processing requests. If a load balancer continues to send traffic to a dead server, it creates a black hole where requests simply time out, degrading the user experience. To mitigate this, load balancers implement health checks—frequent probes that verify the operational status of backend services. These checks might include sending a simple ping, verifying that a specific heartbeat endpoint returns a 200 OK status, or checking the server's local resource utilization metrics. When a server fails these checks consecutively, it is marked as unhealthy and removed from the active rotation list. Once the server passes enough consecutive probes, it is automatically re-introduced. This automated lifecycle management is the backbone of high-availability systems, allowing for self-healing infrastructure that can tolerate individual component failures without manual intervention.

# Simple health check probe logic
server_status = {'s1': True, 's2': False}

def filter_healthy(pool):
    # Return only nodes that are marked as active
    return [s for s in pool if server_status.get(s, False)]

# Route only to functional nodes
print(f"Available: {filter_healthy(['s1', 's2'])}")

Session Persistence and State

In many web applications, the server needs to remember the state of a user across multiple requests. This is often handled through session tokens or cookies. If a load balancer sends a user's first request to Server A and their second request to Server B, the user might lose their session information unless the state is synchronized across all nodes. Session persistence, or 'sticky sessions,' solves this by ensuring that all requests from a specific client are routed to the same backend server for the duration of a session. While convenient for legacy stateful architectures, it introduces a significant drawback: it can lead to uneven load distribution if certain clients generate significantly more traffic than others. Modern, scalable system design typically favors stateless backend services where state is stored in an external distributed cache rather than on the server, allowing the load balancer full freedom in distribution.

# Sticky session using a client identifier hash
def get_persistent_server(client_id, server_pool):
    # Hash the client_id to ensure the same server is always picked
    index = hash(client_id) % len(server_pool)
    return server_pool[index]

print(get_persistent_server("user_123", ['s1', 's2']))

Scaling Through Layers

As traffic volumes grow, even the load balancer itself can become a bottleneck. This is why architects often implement multi-layer load balancing. Layer 4 balancing works at the transport layer, focusing on IP addresses and ports to distribute traffic quickly with minimal overhead. Layer 7 balancing, however, inspects the actual content of the request, such as HTTP headers or URL paths, to make sophisticated routing decisions. By combining these, one might use a hardware load balancer or a high-performance router at Layer 4 to distribute initial traffic across a cluster of software-based Layer 7 proxies. This tiered approach allows the system to handle millions of concurrent connections by offloading TLS termination and path-based routing to specialized software instances while keeping the entry point lightweight and resilient. This architectural design pattern ensures that no single layer of the stack is forced to perform more processing than its capabilities allow.

# L7 Routing based on URL path
routes = {'/api': 'api_server', '/web': 'web_server'}

def route_request(path):
    # Inspect path to direct traffic
    return routes.get(path, 'default_server')

print(f"Directing to: {route_request('/api')}")

Key points

  • Load balancers act as the primary interface to distribute incoming traffic across multiple backend instances.
  • Efficient distribution prevents resource starvation and optimizes total system utilization.
  • Static algorithms like Round Robin are easy to implement but may ignore the actual capacity of individual servers.
  • Dynamic algorithms improve performance by routing requests based on real-time metrics like connection counts.
  • Health checks are critical for identifying and removing faulty servers from the request pool automatically.
  • Sticky sessions provide stateful persistence but can result in imbalanced traffic across the server cluster.
  • Stateless architectures are preferred because they enable the load balancer to distribute traffic freely without affinity constraints.
  • Layered balancing strategies allow for specialized handling of traffic by combining network-level and application-level routing.

Common mistakes

  • Mistake: Thinking load balancers are only for web traffic. Why it's wrong: They can manage database connections, gRPC calls, and even message queues. Fix: Understand that load balancers operate at different layers (L4/L7) to manage diverse traffic types.
  • Mistake: Assuming round-robin is always the best strategy. Why it's wrong: It ignores the actual current load or capacity of a server, leading to hot spots. Fix: Use weighted algorithms or least-connections strategies based on real-time server metrics.
  • Mistake: Neglecting to implement health checks. Why it's wrong: Traffic is sent to dead or unresponsive nodes, causing latency and errors. Fix: Configure active and passive health checks to remove unhealthy instances from rotation automatically.
  • Mistake: Ignoring session stickiness (affinity) requirements. Why it's wrong: Users lose state if the load balancer routes them to a different node mid-session. Fix: Use session cookies or database-backed session storage to ensure consistency.
  • Mistake: Treating the load balancer as a single point of failure. Why it's wrong: If the load balancer goes down, the entire system becomes inaccessible. Fix: Always deploy load balancers in a highly available, redundant pair with a virtual IP and automated failover.

Interview questions

What is a load balancer, and why is it necessary in a distributed system?

A load balancer acts as a traffic cop sitting in front of your servers, routing client requests across all servers capable of fulfilling those requests in a manner that maximizes speed and capacity utilization. It is necessary because it ensures no single server bears too much demand, which prevents bottlenecks, improves responsiveness, and increases availability. By distributing the load, we ensure the system remains resilient and can handle traffic spikes effectively without crashing individual nodes.

Explain the difference between Layer 4 and Layer 7 load balancing.

Layer 4 load balancing operates at the transport layer, focusing on network information like IP addresses and TCP/UDP ports. It is extremely fast because it makes routing decisions without inspecting the content of the packets. In contrast, Layer 7 load balancing operates at the application layer, inspecting HTTP headers, cookies, or URL paths. Layer 7 is smarter because it enables complex routing decisions, such as sending traffic based on the specific type of request, like routing video traffic to a different backend cluster than image traffic.

Compare Round Robin scheduling with Least Connections scheduling.

Round Robin is the simplest algorithm, cycling requests sequentially to each server in the pool. It assumes all servers have equal processing power and that requests take equal time, which is rarely true in real-world systems. Least Connections is more sophisticated, as it tracks the number of active connections per server and sends the next request to the server with the fewest active sessions. This is superior for applications where request duration varies significantly, ensuring that a server processing a 'heavy' task isn't overwhelmed while others sit idle.

How does a load balancer handle session persistence, often called 'sticky sessions'?

Sticky sessions ensure that a specific client is always directed to the same backend server for the duration of their session. This is typically managed by the load balancer inserting a cookie into the client's browser or by hashing the client's source IP address. While useful for applications that store local state in memory, it creates a challenge: if that specific server fails, the user's state is lost. Modern architectures prefer externalizing state into a distributed cache like Redis to avoid this dependency.

Describe the concept of 'Health Checks' in a load balancing configuration.

Health checks are automated, periodic probes sent by the load balancer to every backend server to verify its operational status. A typical configuration might look like this: 'GET /health HTTP/1.1'. If a server fails to respond within a timeout or returns an error code, the load balancer marks it as 'unhealthy' and stops sending traffic to it. This is crucial for high availability, as it prevents the system from sending requests to broken instances, allowing for seamless maintenance and automatic recovery.

How do you mitigate the load balancer itself becoming a single point of failure in your architecture?

To prevent the load balancer from being a single point of failure, we implement redundancy by deploying a cluster of load balancers, often using an Active-Passive or Active-Active configuration. We use a virtual IP address (VIP) managed by a protocol like VRRP (Virtual Router Redundancy Protocol). If the primary load balancer stops sending heartbeats, the standby node immediately assumes the VIP. This failover process ensures that the entry point to our system remains highly available even if hardware or network issues strike the primary instance.

All System Design interview questions →

Check yourself

1. Which scenario makes an 'Least Connections' algorithm significantly better than a 'Round Robin' algorithm?

  • A.When all backend servers have identical hardware specifications.
  • B.When requests vary significantly in their processing time or computational complexity.
  • C.When the network latency between the load balancer and servers is uniform.
  • D.When the system is operating at very low traffic volume.
Show answer

B. When requests vary significantly in their processing time or computational complexity.
Least Connections tracks active requests, preventing servers handling 'heavy' tasks from being overwhelmed. Round Robin is fine for identical, fast tasks, but fails when requests differ in cost. The others don't necessitate switching algorithms.

2. In a Layer 7 load balancing configuration, what is a primary advantage over Layer 4 balancing?

  • A.Lower latency due to reduced packet inspection.
  • B.Ability to route traffic based on URL paths, headers, or content types.
  • C.Compatibility with non-TCP protocols like raw UDP streams.
  • D.Significantly lower computational overhead on the load balancer itself.
Show answer

B. Ability to route traffic based on URL paths, headers, or content types.
Layer 7 (Application Layer) can inspect the payload to make intelligent routing decisions (e.g., sending /api traffic to one pool and /static to another). Layer 4 is faster but 'blind' to content. Layer 4 handles UDP better, and L7 is more computationally intensive.

3. Why is it dangerous to rely solely on DNS-based load balancing for a highly available production system?

  • A.DNS servers are not capable of resolving multiple IP addresses.
  • B.DNS lookups happen too frequently and cause excessive network traffic.
  • C.DNS caching at the client or ISP level prevents immediate failover when a server goes down.
  • D.DNS protocols do not support weighted distributions.
Show answer

C. DNS caching at the client or ISP level prevents immediate failover when a server goes down.
DNS entries have TTLs (Time to Live). If a server fails, clients will continue trying to reach it until their local cache expires. Options 1, 2, and 4 are technically incorrect regarding DNS capabilities.

4. How does the 'Session Persistence' (or sticky sessions) feature impact system design?

  • A.It improves horizontal scalability by distributing traffic perfectly across all nodes.
  • B.It allows stateless services to run without needing an external database.
  • C.It reduces the load balancer's CPU usage by offloading hashing to the client.
  • D.It makes it harder to achieve a balanced distribution if some users have very long-running sessions.
Show answer

D. It makes it harder to achieve a balanced distribution if some users have very long-running sessions.
Sticky sessions bind a user to a specific node, which can create 'hot spots' if one user is very active. It does not improve scalability (Option 1), has no relation to database necessity (Option 2), and actually increases load balancer state tracking (Option 3).

5. When designing for high availability, what is the purpose of placing a load balancer in front of a cluster of servers?

  • A.To encrypt all traffic automatically using hardware-based certificates.
  • B.To provide a single entry point that can route around individual node failures.
  • C.To convert all incoming traffic to a single protocol like HTTP/2.
  • D.To act as a primary storage node for distributed file systems.
Show answer

B. To provide a single entry point that can route around individual node failures.
The load balancer abstracts the cluster, allowing nodes to be added, removed, or replaced without the client knowing. Encryption is a feature but not the primary goal of the architectural pattern, and it is not a file storage tool.

Take the full System Design quiz →

← PreviousScalability — Horizontal vs Vertical ScalingNext →Caching Strategies — CDN, Redis, Memcached

System Design

31 lessons, free to read.

All lessons →

Track your progress

Sign in to mark lessons done, score quizzes and keep notes.

Open in the app