Cluster Deployment

RustPBX supports multi-node clustering for registration state synchronization, distributed SipFlow capture, and cross-node call control.

1. Architecture Overview

           ┌──────────────┐
           │  Load Balancer│
           │  (SIP/HTTP)  │
           └──────┬───────┘
      ┌───────────┼───────────┐
      ▼           ▼           ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Node 1   │ │ Node 2   │ │ Node 3   │
│ SIP+HTTP │◄─► SIP+HTTP│◄─► SIP+HTTP│
│ SipFlow  │ │ SipFlow  │ │ SipFlow  │
└──────────┘ └──────────┘ └──────────┘
      │           │           │
      └───────────┼───────────┘
                  ▼
         ┌──────────────┐
         │  MySQL/PgSQL │
         └──────────────┘

Key capabilities:

  • Inter-node event sync via SIP MESSAGE for registration/presence
  • SipFlow supports consistent hashing by call_id to route to specific capture nodes
  • Shared database (MySQL/PostgreSQL) ensures config and CDR consistency

2. Database Requirements

Cluster mode does not support SQLite — a shared database is required:

DatabaseMin VersionRecommendedNotes
MySQL8.0+8.4 LTSCharset utf8mb4, collation utf8mb4_unicode_ci
PostgreSQL14+16Enable pg_stat_statements for monitoring

Connection string examples:

# MySQL
database_url = "mysql://rustpbx:password@10.0.0.100:3306/rustpbx"

# PostgreSQL
database_url = "postgres://rustpbx:password@10.0.0.100:5432/rustpbx"

Important notes:

  • All nodes must connect to the same database instance
  • SeaORM auto-runs migrations on first startup; subsequent nodes detect and skip completed migrations
  • DDL files in the source root (rustpbx_ddl_*.sql) are available for manual schema creation

3. Cluster Configuration

3.1 Inter-Node Event Sync

Configure peer nodes in [cluster] section of config.toml:

[cluster]
peers = [
  { addr = "10.0.0.2", sip_port = 5060, ami_port = 8080 },
  { addr = "10.0.0.3", sip_port = 5060, ami_port = 8080 },
]

Each node only lists the other nodes — do not include itself. Nodes exchange JSON events via SIP MESSAGE (application/x-rustpbx-cluster-event).

Synced event types:

  • Locator events: user registration/unregistration state
  • Presence events: user online/offline/busy status

3.2 SipFlow Cluster Mode

[sipflow]
[sipflow.remote]
flush_interval_secs = 5

[[sipflow.remote.nodes]]
udp = "10.0.0.2:6060"
http = "http://10.0.0.2:6060"

[[sipflow.remote.nodes]]
udp = "10.0.0.3:6060"
http = "http://10.0.0.3:6060"
  • Each proxy node can read and write; requests are routed by call_id consistent hashing
  • Deploy the standalone sipflow binary (from src/bin/sipflow.rs) as the capture server

3.3 Core Config Per Node

http_addr = "0.0.0.0:8080"
database_url = "mysql://rustpbx:password@dbhost:3306/rustpbx"
external_ip = "10.0.0.1"

[proxy]
addr = "0.0.0.0"
udp_port = 5060
modules = ["acl", "auth", "registrar", "call", "presence"]
media_proxy = "auto"

[console]
session_secret = "your-secret-here"  # Must be identical across all nodes

3.4 Distributed Session Registry (0.5+)

Beyond locator/presence sync, 0.5 wires a cluster-wide session registry end to end:

ClusterFlow
  • Owner-routed operations: any node can answer, transfer, hang up, or set variables on a call it does not host — the request is forwarded to the node that owns the session automatically. A node failure marks its sessions offline instead of leaving them ghosted.
  • Session user_data replication: application data attached to a session (CRM ids, campaign tags, agent context) is replicated to peer nodes. Transfers and sub-sessions inherit it, so business context survives node handoffs.
  • Heartbeat gauges: each node reports liveness of the sessions it owns; the AMI health endpoint exposes these gauges for monitoring, and self-pruned locator bindings are reported as offline.

Application integration point: PUT /api/calls/active/{session_id}/userdata and GET /static/docs/rustpbx/static/docs/userdata work from any node — the op is routed to the owner node transparently.

4. Load Balancing

4.1 SIP Traffic

Use a SIP Load Balancer (OpenSIPS, Kamailio) or DNS SRV:

  • Source IP hash: ensures REGISTER messages from the same user reach the same node (reduces cross-node sync)
  • Call-ID hash: ensures signaling for the same call stays on the same node

4.2 HTTP Traffic

Use Nginx/HAProxy reverse proxy:

upstream rustpbx {
    ip_hash;  # Session affinity
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

5. Deployment Steps

# 1. Prepare shared database
mysql -u root -p -e "CREATE DATABASE rustpbx CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# 2. Deploy on each node
docker pull docker.cnb.cool/miuda.ai/rustpbx:latest

# 3. Node 1 (10.0.0.1)
docker run -d --name rustpbx \
  -v $(pwd)/config.toml:/app/config.toml \
  -v $(pwd)/config:/app/config \
  -p 8080:8080 -p 5060:5060/udp \
  docker.cnb.cool/miuda.ai/rustpbx:latest

# 4. Node 2/3: same steps, adjust external_ip and cluster.peers

6. Always-On Cluster Event Hub

The cluster event hub is provisioned even in single-node mode (cluster.peers is empty or the entire [cluster] section is omitted). This means:

  • Local event dispatch: locator (registration/unregistration) and presence (online/offline/busy) events are always dispatched to addon handlers through the hub, regardless of cluster mode.
  • Cross-node sync: only activates when cluster.peers lists at least one peer node. Events are exchanged via SIP MESSAGE (application/x-rustpbx-cluster-event).
  • CC addon integration: the Contact Center addon now wires the active_call_registry from the event hub, enabling CTI control endpoints (hold, transfer, record, mute) to function reliably. Previously these could return 503 in certain topologies.

This design ensures that addons depending on event dispatch work identically in both single-node and clustered deployments.

7. Operations

  • Database connection pool: each node’s default pool is managed by SeaORM; total nodes × pool_size must not exceed database max_connections
  • Time sync: all nodes must use NTP — CDR timestamps depend on system clocks
  • Config consistency: config/trunks/, config/routes/ etc. should be synced to all nodes via shared storage or GitOps
  • Rolling restart: restart nodes one at a time, ensuring at least one node remains online
  • SipFlow storage: plan disk capacity per capture node independently (~50-200KB signaling data per call)

8. Cluster AMI endpoints (commercial)

Multi-node deployments expose additional cluster management endpoints under {ami_path}:

MethodPathDescription
POST/cluster/pingPing all peer nodes for health
GET/cluster/reload_configSSE-based cluster config reload stream
POST/cluster/reload_syncSync JSON reload payload to a specific peer
POST/cluster/dispatch_commandDispatch a call control command to a peer node
POST/cluster/session_opOwner-routed RWI command envelope (terminal applies locally)
GET/cluster/show_session/{session_id}Show detailed call session state on a peer
GET/cluster/session_owner/{call_id}Resolve which node hosts a call
GET/cluster/list_callsList all active calls across the cluster (cluster-aware GET /calls query)
POST/cluster/set_userdata / get_userdataOwner-routed session user-data replication
POST/cluster/set_var / get_varOwner-routed call variables
GET/cluster/logs/recent · /cluster/logs/followRecent and streaming logs from peer nodes
GET/cluster/evaluate_routeRoute evaluation against the cluster routing stack

These endpoints enable centralized call control, cross-node session inspection, coordinated configuration pushes, and user-data replication from any peer in the cluster.