All posts
ScalingApr 30, 2026·6 min read

Load Balancing Node.js Applications

Round-robin vs least-connections vs IP-hash, sticky sessions, health checks, and Nginx vs AWS ALB vs Cloudflare for Node.js load balancing in production.

Why you need a load balancer

A load balancer sits between your users and your Node.js instances, distributing traffic and routing away from unhealthy instances. Without one, scaling horizontally is impossible and a single instance failure means full downtime.

Load balancing algorithms

Round-robin — Requests distributed evenly. Best for stateless apps where all requests have similar cost. The default and right choice for most REST APIs.

Least connections — Routes new requests to the instance with fewest active connections. Better for variable-duration requests (long-polling, file uploads).

IP-hash — Same client IP always hits the same instance. Use for WebSocket connections where reconnecting to a different server would break state. Avoid for REST APIs — it breaks even distribution if some IPs have high traffic.

Health checks

Load balancers need a health check endpoint to know which instances to route to. Configure the LB to check /health every 10–30 seconds, with a threshold of 2–3 consecutive failures before removing from rotation.

Nginx configuration

upstream nodejs_cluster {
  least_conn;
  server 10.0.0.1:3000 max_fails=3 fail_timeout=30s;
  server 10.0.0.2:3000 max_fails=3 fail_timeout=30s;
  server 10.0.0.3:3000 max_fails=3 fail_timeout=30s;
  keepalive 32;
}

server {
  listen 443 ssl http2;
  location / {
    proxy_pass http://nodejs_cluster;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";  # For WebSockets
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  location /health {
    proxy_pass http://nodejs_cluster;
    access_log off;
  }
}

Trusting X-Forwarded-For

When your app is behind a load balancer, req.ip will be the LB's IP, not the client's. Express's trust proxy setting reads the X-Forwarded-For header instead.

// Trust one hop of proxy (behind your own LB)
app.set('trust proxy', 1);
// Now req.ip returns the real client IP

Managed options

AWS ALB — Integrates with Auto Scaling, supports path-based routing and weighted target groups for canary deployments. Good for AWS-native stacks.

Cloudflare — Global load balancing with built-in DDoS protection, health checks, and geographic routing. Good for latency-sensitive consumer apps.

Platform-managed — Container platforms (including Cloudoku) handle load balancing automatically as part of the deployment abstraction.

Ready to put this into practice?

Deploy your Node.js app to production in minutes — zero YAML, automatic CI/CD, and HTTPS included.