Skip to main content

Get started in 5 minutes

This guide will have you running the load balancer with three test backend servers and making requests in under 5 minutes.
1

Install Bun runtime

The load balancer requires the Bun runtime. Install it if you haven’t already:
curl -fsSL https://bun.sh/install | bash
Verify installation:
bun --version
2

Clone and install dependencies

Clone the repository and install dependencies:
git clone https://github.com/<your-username>/loadbalancer.git
cd loadbalancer
bun install
3

Start test backend servers

Open three separate terminal windows and start simple HTTP servers on ports 3001, 3002, and 3003:
bun -e "Bun.serve({ port: 3001, fetch: () => new Response('Hello from Backend 3001') })"
These are minimal test servers that return a simple text response. In production, these would be your actual application servers.
4

Start the load balancer

In a fourth terminal window, start the load balancer:
bun run start
You should see output like this:
INFO: Load balancer running on port 3000
INFO: Backend servers: http://localhost:3001, http://localhost:3002, http://localhost:3003
INFO: Health checker started (checking every 5s)
HEALTH: HEALTHY http://localhost:3001 - status: 200
HEALTH: HEALTHY http://localhost:3002 - status: 200
HEALTH: HEALTHY http://localhost:3003 - status: 200
5

Test with curl

Send requests to the load balancer and watch traffic distribute across backends:
curl http://localhost:3000
curl http://localhost:3000
curl http://localhost:3000
Expected output (round-robin distribution):
Hello from Backend 3001
Hello from Backend 3002
Hello from Backend 3003
The load balancer logs will show each request:
REQUEST: GET / -> http://localhost:3001
RESPONSE: GET / <- http://localhost:3001 [200] 12ms
REQUEST: GET / -> http://localhost:3002
RESPONSE: GET / <- http://localhost:3002 [200] 8ms
REQUEST: GET / -> http://localhost:3003
RESPONSE: GET / <- http://localhost:3003 [200] 7ms
6

Test automatic failover

Stop one of the backend servers (press Ctrl+C in its terminal). Wait 5 seconds for the health check to detect the failure.Send more requests:
curl http://localhost:3000
curl http://localhost:3000
curl http://localhost:3000
Traffic will automatically route only to the healthy backends. The logs will show:
HEALTH: UNHEALTHY http://localhost:3002 - Connection refused
REQUEST: GET / -> http://localhost:3001
RESPONSE: GET / <- http://localhost:3001 [200] 10ms
REQUEST: GET / -> http://localhost:3003
RESPONSE: GET / <- http://localhost:3003 [200] 9ms
REQUEST: GET / -> http://localhost:3001
RESPONSE: GET / <- http://localhost:3001 [200] 11ms
Restart the backend server and watch it automatically rejoin the healthy pool within 5 seconds.

Understanding the code

Here’s how the components wire together in src/index.ts:
src/index.ts
import express from "express";
import { ProxyHandler } from "./proxy/proxyHandler.ts"
import { LoadBalancer } from "./balancer/loadBalancer.ts";
import { BackendPool } from "./balancer/pool.ts";
import { RoundRobin } from "./balancer/roundRobin.ts";
import { HealthChecker } from "./healthchecker/healthChecker.ts";
import { Logger } from "./utils/logger.ts";

const app = express();

// Define backend server URLs
const backendUrls = [
    "http://localhost:3001",
    "http://localhost:3002",
    "http://localhost:3003"
];

// Initialize the backend pool
const backendPool = new BackendPool(backendUrls);

// Configure the load balancing strategy (round-robin)
const strategy = new RoundRobin();

// Create the load balancer with the pool and strategy
const loadBalancer = new LoadBalancer(backendPool, strategy);

// Start health checker (checks every 5 seconds)
const healthChecker = new HealthChecker(backendPool, 5000);
healthChecker.start();
Logger.info("Health checker started (checking every 5s)");

// Route all requests through the proxy handler
app.use("/", ProxyHandler(loadBalancer, backendPool))

// Start listening on port 3000
const PORT = 3000;
app.listen(PORT, () => {
    Logger.info(`Load balancer running on port ${PORT}`);
    Logger.info(`Backend servers: ${backendUrls.join(", ")}`);
});

Key components explained

BackendPool — Manages the list of backend servers and their health status. Each backend has a URL and a boolean health flag.
src/balancer/pool.ts
export class BackendPool {
    private backends: Backend[];

    constructor(urls: string[]){
        this.backends = urls.map((url: string) => ({
            url, health:true
        }))
    }

    getHealthyBackends(): Backend[] {
        return this.backends.filter(backend => backend.health);
    }

    markUnhealthy(url: string): void {
        const backend = this.backends.find(b => b.url === url);
        if(backend) backend.health = false;
    }

    markHealthy(url: string): void {
        const backend = this.backends.find(b => b.url === url);
        if(backend) backend.health = true;
    }
}
RoundRobin — Implements the load balancing algorithm. Maintains an index that increments with each request.
src/balancer/roundRobin.ts
export class RoundRobin {
    private index = 0;

    pick(backends: Backend[]): Backend {
        if (backends.length === 0) {
            throw new Error("No backend is available");
        }

        const backend = backends[this.index % backends.length]!;
        this.index++;

        return backend;
    }
}
LoadBalancer — Orchestrates the pool and strategy to select a backend for each request.
src/balancer/loadBalancer.ts
export class LoadBalancer {
    constructor(
        private backendPool: BackendPool,
        private strategy: RoundRobin
    ){}

    pickBackend(): Backend {
        const healthyBackends = this.backendPool.getHealthyBackends();
        return this.strategy.pick(healthyBackends);
    }
}

Development mode

For development with automatic reload on code changes:
bun run dev
This uses Bun’s --watch flag to restart the server when source files change.

Next steps

Installation guide

Learn about prerequisites and project structure

Architecture deep dive

Understand the internal design decisions

Configuration

Customize backend URLs and health check intervals

Extending strategies

Add new load balancing algorithms

Build docs developers (and LLMs) love