PBX · OpenSIPS 3.x
Use DIDHub as an OpenSIPS dispatcher upstream
OpenSIPS 3.x ships with the same dispatcher primitive as Kamailio but with different syntax (DB-driven by default, OPTIONS keepalive built in). This tutorial wires up DIDHub as a load-balanced upstream with primary + secondary PoPs and digest auth.
/etc/opensips/opensips.cfg (relevant sections)
loadmodule "dispatcher.so" loadmodule "db_text.so" # or db_mysql / db_postgres loadmodule "uac.so" loadmodule "uac_auth.so" loadmodule "tm.so" loadmodule "rr.so" loadmodule "sl.so" modparam("dispatcher", "db_url", "text:///etc/opensips/dbtext") modparam("dispatcher", "ds_ping_interval", 30) modparam("dispatcher", "ds_ping_method", "OPTIONS") modparam("dispatcher", "ds_probing_threshold", 3) modparam("dispatcher", "ds_probing_mode", 1) # probe all gateways # uac_auth challenges — needed when DIDHub runs in digest mode (not IP ACL) modparam("uac_auth", "credential", "trunk_xxxxx:sip.didhub.io:<PASSWORD>")
/etc/opensips/dbtext/dispatcher
# id(int)setid(int)destination(string)flags(int)priority(int)attrs(string)description(string)
1:1:sip:sip.didhub.io;transport=udp:0:100:weight=70:didhub-primary
2:1:sip:sip-bk.didhub.io;transport=udp:0:50:weight=30:didhub-secondary
3:2:sip:sip-eu.didhub.io;transport=udp:0:100:weight=100:didhub-eu-fallback
Outbound route
route[OUTBOUND_PSTN] {
# Pick best alive destination from set 1, weight-based
if (!ds_select_dst("1", "4")) { # 4 = weight + alive flags
send_reply(503, "All upstreams down");
exit;
}
# Strip leading + and re-add for E.164 normalization
if ($rU =~ "^\+") {
$rU = $(rU{s.substr,1,0});
}
$ru = "sip:+" + $rU + "@" + $du;
t_on_failure("DISPATCH_FAILOVER");
t_relay();
}
failure_route[DISPATCH_FAILOVER] {
# Retry next upstream on 5xx or timeout
if (t_was_cancelled()) exit;
if (t_check_status("(408|5[0-9][0-9])")) {
if (ds_next_dst()) {
t_on_failure("DISPATCH_FAILOVER");
t_relay();
}
}
if (t_check_status("401|407")) {
# Digest challenge from DIDHub
uac_auth();
t_relay();
}
}
Trigger the route from your main script
route {
if (!mf_process_maxfwd_header("10")) {
send_reply(483, "Too Many Hops");
exit;
}
if (has_totag()) {
loose_route();
t_relay();
exit;
}
record_route();
# Route +E.164 to PSTN upstream
if ($rU =~ "^\+?[0-9]{6,15}$") {
route(OUTBOUND_PSTN);
exit;
}
send_reply(404, "Not Found");
}
Verify dispatcher health
opensips-cli -x mi ds_list # Expected output: SET:: 1 DEST:: sip:sip.didhub.io;transport=udp PRIORITY:: 100 FLAGS:: AP # A=Active P=Probing DEST:: sip:sip-bk.didhub.io;transport=udp PRIORITY:: 50 FLAGS:: AP
Real failover test
- Place an outbound call. Confirm it routes via
sip.didhub.io(the higher-weighted destination). - Block
sip.didhub.ioat your firewall. - Within ~90 seconds (3 probes × 30s) OpenSIPS marks it inactive (
FLAGS:: P). - Subsequent calls automatically use
sip-bk.didhub.io. - Unblock; OpenSIPS re-marks active on the next successful OPTIONS response.
Tips
- db_mysql instead of db_text: for clustered OpenSIPS deployments use
db_mysqlwith thedispatchertable from/etc/opensips/sql/standard-create.sql. - Per-destination max calls: use the
attrsfield withmax_calls=Nand check viads_count_dst(). - Same upstream as Kamailio? The DIDHub trunk credentials are identical — you can run OpenSIPS and Kamailio on the same trunk simultaneously (e.g. as front-end + back-end).
Compare to Kamailio: see the Kamailio dispatcher tutorial. The two configs are almost identical at the routing-logic level, but OpenSIPS 3.x has cleaner built-in probing semantics and DB-backed dispatcher tables out of the box.
Ready to get a number?
Pick a DID in 130+ countries from $1.99/month. Activates instantly on most numbers.