Skip to content

🌐 Multi-Server Web Cluster & Load Balancing

8 min readUpdated: Sep 26, 2026
View as Markdown

Enterprise guide for deploying multiple Ring2All Web and API nodes behind Cloudflare and Nginx for high availability, zero-downtime maintenance, and horizontal scaling.


  1. Architectural Overview
  2. Why Ring2All is 100% Stateless
  3. Cloudflare Load Balancing Options
  4. Step-by-Step Multi-Node Deployment
  5. Nginx Configuration on Web Nodes
  6. Database & Redis Clustering Requirements
  7. Operational Verification & Failover Testing
  8. Summary & Key Advantages

In high-density service provider environments, the web management layer (Ring2All PBX Admin, User Portal, Switchboard Console, and BSS Client Portal) can be scaled horizontally across multiple independent Linux instances without requiring distinct domain names.

A single canonical domain (e.g., *.ring2all.com or app.ring2all.com) serves all web traffic, distributed intelligently across backend nodes by Cloudflare Edge Load Balancing:

flowchart TD
    subgraph Clients["Global Ingress (Internet)"]
        Browser1["Client Browser A<br/>mycompany.ring2all.com"]
        Browser2["Client Browser B<br/>acme.ring2all.com"]
        MobileApp["User Portal Mobile PWA"]
    end

    subgraph Edge["Cloudflare Anycast Global Edge Network"]
        CF_WAF["Cloudflare WAF & DDoS Shield"]
        CF_SSL["Universal SSL Termination (*.ring2all.com)"]
        CF_LB["Cloudflare Load Balancer / Round-Robin Pool"]
    end

    subgraph WebCluster["Stateless Web & API Tier (Port 443 / 80)"]
        Node1["Web Node 01 (198.51.100.20)<br/>Nginx + softswitch-web + softswitch-api"]
        Node2["Web Node 02 (198.51.100.21)<br/>Nginx + softswitch-web + softswitch-api"]
        NodeN["Web Node N (198.51.100.2N)<br/>Horizontal Scale on Demand"]
    end

    subgraph DataTier["Centralized State & Storage Layer"]
        PG[("PostgreSQL 17 HA Cluster<br/>ss_admin | ss_telephony | ss_billing")]
        Redis[("Redis Cluster / PubSub<br/>Session Cache & WebSocket Broker")]
    end

    subgraph TelephonyTier["Perimeter VoIP (Bypasses Web Proxy)"]
        SBC["Ring2All SBC (Kamailio 6.1)<br/>sbc.ring2all.com (Port 5060/5061)"]
    end

    Browser1 --> CF_WAF
    Browser2 --> CF_WAF
    MobileApp --> CF_WAF

    CF_WAF --> CF_SSL
    CF_SSL --> CF_LB

    CF_LB -- "Round-Robin / Health Check" --> Node1
    CF_LB -- "Round-Robin / Health Check" --> Node2
    CF_LB -- "Round-Robin / Health Check" --> NodeN

    Node1 --> PG
    Node2 --> PG
    NodeN --> PG

    Node1 --> Redis
    Node2 --> Redis
    NodeN --> Redis

Unlike legacy PBX platforms that store web sessions in server memory files (/tmp/sess_*), Ring2All’s application tier is designed with zero in-memory session lock-in:

  1. Cryptographic JWT Authentication (Stateless):
    • When an administrator or tenant user logs in, the API issues a signed JSON Web Token (JWT).
    • Any Web Node can decode and cryptographically verify the token using the shared cluster secret key (JWT_SECRET).
    • Request #1 can hit Web Node 01 and Request #2 can hit Web Node 02 without requiring sticky sessions or session re-authentication.
  2. Single Page Application (SPA) Decoupling:
    • The frontend interfaces (softswitch-web, softswitch-portal, softswitch-switchboard, softswitch-bss-client) compile into static HTML5, CSS3, and JavaScript bundles.
    • Static assets are heavily cached across Cloudflare’s 300+ Edge data centers. Over 80% of asset requests never reach your origin servers.
  3. Centralized Data & Cache Tier:
    • Real-time events, WebSockets, and database transactions are offloaded to PostgreSQL 17 and Redis, leaving the Node.js Fastify API threads free to execute sub-millisecond JSON operations.

You can implement multi-server web distribution using either of the following two Cloudflare configurations:

Option A: Cloudflare Anycast Round-Robin (100% Free on all Cloudflare Plans)

Section titled “Option A: Cloudflare Anycast Round-Robin (100% Free on all Cloudflare Plans)”

By creating multiple A records with the same hostname pointing to distinct server IPs, Cloudflare distributes incoming HTTPS connections evenly across your origins:

Record Type Host / Name Origin Server IP Cloudflare Proxy Status
A * (Wildcard) 198.51.100.20 (Web Node 01) ☁️ Proxied (Orange Cloud)
A * (Wildcard) 198.51.100.21 (Web Node 02) ☁️ Proxied (Orange Cloud)
A * (Wildcard) 198.51.100.22 (Web Node 03) ☁️ Proxied (Orange Cloud)
  • How it works: Cloudflare Anycast automatically balances client HTTP sessions among the configured origin IPs.
  • Cost: Included at zero additional charge in standard Cloudflare accounts.

Option B: Cloudflare Load Balancer (With Active Health Checks & Auto-Failover)

Section titled “Option B: Cloudflare Load Balancer (With Active Health Checks & Auto-Failover)”

For mission-critical telecom carriers, the Cloudflare Load Balancing add-on provides automated health monitoring and sub-3-second failover:

  1. Origin Pool Configuration:
    • Create pool: ring2all-web-cluster.
    • Add Origins: node-01 (198.51.100.20), node-02 (198.51.100.21).
    • Weight: 1:1 (or customized by server capacity).
  2. Health Monitor:
    • Path: /health (or /api/health).
    • Expected Response Code: 200 OK.
    • Interval: Every 15 seconds.
    • Timeout: 3 seconds.
  3. Autonomous Failover:
    • If node-01 fails or undergoes routine OS maintenance, Cloudflare automatically stops routing requests to it within 3 seconds.
    • Zero User Interruption: 100% of traffic immediately routes to node-02 without connection dropouts or 502 Bad Gateway errors.

Deploy 2 or more Linux servers (Debian 13 or Ubuntu 24.04 LTS) with standard system specs:

  • vCPU: 4 cores.
  • RAM: 8 GB.
  • Disk: 50 GB NVMe SSD.

On each web node, install the system packages:

Terminal window
# Update repository and install application packages
sudo apt update
sudo apt install -y softswitch-web softswitch-api softswitch-portal softswitch-switchboard nginx

Ensure that /opt/softswitch-api/.env on all web nodes contains identical cryptographic and database credentials:

# /opt/softswitch-api/.env (Must match across ALL Web Nodes)
NODE_ENV=production
PORT=3000
HOST=127.0.0.1
# Shared Cryptographic JWT Key (CRITICAL: Must be identical)
JWT_SECRET="r2a_prod_super_secret_cluster_token_98374291834"
# Centralized PostgreSQL 17 Cluster Connection
DATABASE_URL="postgres://ring2all_app:SecureClusterPass@192.168.10.31:5432/ss_telephony"
ADMIN_DATABASE_URL="postgres://ring2all_app:SecureClusterPass@192.168.10.31:5432/ss_admin"
BILLING_DATABASE_URL="postgres://ring2all_app:SecureClusterPass@192.168.10.31:5432/ss_billing"
# Shared Redis Cluster
REDIS_HOST="192.168.10.31"
REDIS_PORT=6379
REDIS_PASSWORD="ClusterRedisAuthPass2026"

On each web node:

Terminal window
sudo systemctl daemon-reload
sudo systemctl enable --now softswitch-api
sudo systemctl restart nginx

Every web node runs a lightweight local Nginx reverse proxy that handles incoming traffic forwarded from Cloudflare:

/etc/nginx/sites-available/ring2all-cluster-node.conf
server {
listen 80;
listen [::]:80;
server_name *.ring2all.com ring2all.com;
# Trust Cloudflare proxy IP headers
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
real_ip_header CF-Connecting-IP;
# Static Web Frontend
root /opt/softswitch-web/dist;
index index.html;
# Gzip Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Health Check Endpoint for Cloudflare Load Balancer
location = /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# API Proxy
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
proxy_connect_timeout 3s;
proxy_read_timeout 60s;
}
# SPA Routing Fallback
location / {
try_files $uri $uri/ /index.html;
}
}

To sustain thousands of concurrent web requests across multiple nodes without connection pool starvation:

  1. PostgreSQL Connection Pool Optimization (pgbouncer):
    • Install pgbouncer on the PostgreSQL database cluster to manage thousands of lightweight connection handles from all web nodes.
    • Configure transaction pooling mode (pool_mode = transaction) in pgbouncer.ini.
  2. Redis Centralized Cache:
    • Configure Redis persistence (appendonly yes) or Redis Sentinel for high availability.
    • Real-time WebSocket notifications (active call dashboards, BLF state changes) broadcast over Redis Pub/Sub so that a user connected to Node 01 instantly receives state updates generated by Node 02.

On each individual web node, execute:

Terminal window
curl -I http://127.0.0.1/health
# Expected: HTTP/1.1 200 OK
curl -I http://127.0.0.1/api/health
# Expected: HTTP/1.1 200 OK

From an external computer, inspect the HTTP headers:

Terminal window
curl -svo /dev/null https://app.ring2all.com/health
# Expected:
# < HTTP/2 200
# < cf-ray: ...
# < server: cloudflare
  1. Stop the web service on Node 01:
    Terminal window
    sudo systemctl stop nginx
  2. Immediately browse to https://app.ring2all.com or send 100 consecutive HTTP requests via ab / wrk.
  3. Cloudflare automatically shunts 100% of traffic to Node 02. The user session remains completely active with zero errors.
  4. Restart Node 01:
    Terminal window
    sudo systemctl start nginx
  5. Cloudflare automatically resumes traffic distribution to Node 01 within 15 seconds.

Feature / Benefit Single Server Multi-Server with Cloudflare
Domain Management 1 Domain Identical Single Domain (*.ring2all.com)
High Availability None (Single Point of Failure) Active-Active Cluster (N+1)
DDoS & Web Shield Local iptables/nftables only Enterprise Cloudflare Global WAF
Maintenance Downtime Requires scheduled outages Zero-Downtime Rolling Restarts
VoIP Telephony Impact Co-located on same box VoIP completely isolated on Kamailio SBC
Horizontal Scalability Vertical upgrade only Add new web nodes in < 5 minutes