[{"data":1,"prerenderedAt":431},["ShallowReactive",2],{"post-\u002Fblog\u002Fkamailio-dispatcher-failover":3},{"id":4,"title":5,"author":6,"body":7,"category":415,"coverImage":416,"date":417,"description":418,"extension":419,"meta":420,"navigation":310,"path":421,"readingTime":422,"seo":423,"stem":424,"tags":425,"__hash__":430},"posts\u002Fblog\u002Fkamailio-dispatcher-failover.md","Building resilient SIP routing with Kamailio dispatcher","Tumarm Engineering",{"type":8,"value":9,"toc":406},"minimark",[10,14,18,21,26,29,40,83,86,106,110,113,119,151,155,162,168,174,178,181,187,196,200,206,273,279,283,286,330,333,365,368,372,375,399,402],[11,12,5],"h1",{"id":13},"building-resilient-sip-routing-with-kamailio-dispatcher",[15,16,17],"p",{},"Most Kamailio deployments that start with a round-robin dispatcher don't stay round-robin for long. When a carrier route degrades — increasing packet loss, rising PDD, occasional 5xx storms — you need Kamailio to detect it and route around it without human intervention. That's what the dispatcher module with active probing gives you.",[15,19,20],{},"This post covers the configuration in enough detail to ship a production setup, not just a working demo.",[22,23,25],"h2",{"id":24},"dispatcherlist-the-basics","dispatcher.list: the basics",[15,27,28],{},"The dispatcher module reads destinations from a file or database. The file format is one destination per line:",[30,31,36],"pre",{"className":32,"code":34,"language":35},[33],"language-text","# \u002Fetc\u002Fkamailio\u002Fdispatcher.list\n# setid  destination            flags  priority\n1        sip:carrier-a.example:5060   0        10\n1        sip:carrier-b.example:5060   0        10\n2        sip:backup.example:5060      0        5\n","text",[37,38,34],"code",{"__ignoreMap":39},"",[41,42,43,55,77],"ul",{},[44,45,46,50,51,54],"li",{},[47,48,49],"strong",{},"setid"," groups destinations. ",[37,52,53],{},"ds_select_dst(1, 4)"," picks from setid 1 using algorithm 4 (round-robin with weights).",[44,56,57,60,61,64,65,68,69,72,73,76],{},[47,58,59],{},"flags"," mark destination state. ",[37,62,63],{},"0"," = active, ",[37,66,67],{},"1"," = inactive, ",[37,70,71],{},"2"," = probing. Kamailio updates these at runtime via ",[37,74,75],{},"ds_set_state()",".",[44,78,79,82],{},[47,80,81],{},"priority"," breaks ties in weight-based algorithms. Higher priority wins when weights are equal.",[15,84,85],{},"Reload the list without restart:",[30,87,91],{"className":88,"code":89,"language":90,"meta":39,"style":39},"language-bash shiki shiki-themes github-light github-dark","kamcmd dispatcher.reload\n","bash",[37,92,93],{"__ignoreMap":39},[94,95,98,102],"span",{"class":96,"line":97},"line",1,[94,99,101],{"class":100},"sScJk","kamcmd",[94,103,105],{"class":104},"sZZnC"," dispatcher.reload\n",[22,107,109],{"id":108},"probing-modes","Probing modes",[15,111,112],{},"Active probing sends SIP OPTIONS to each destination on a configurable interval. When a destination stops responding, Kamailio marks it inactive and stops routing to it.",[30,114,117],{"className":115,"code":116,"language":35},[33],"# kamailio.cfg\nmodparam(\"dispatcher\", \"ds_probing_mode\", 1)\nmodparam(\"dispatcher\", \"ds_ping_interval\", 10)\nmodparam(\"dispatcher\", \"ds_probing_threshold\", 3)\nmodparam(\"dispatcher\", \"ds_inactive_threshold\", 3)\n",[37,118,116],{"__ignoreMap":39},[41,120,121,133,139,145],{},[44,122,123,126,127,129,130,132],{},[37,124,125],{},"ds_probing_mode = 1"," — probe all active and inactive destinations. Use ",[37,128,71],{}," to probe only destinations flagged for probing (flag ",[37,131,71],{}," in dispatcher.list).",[44,134,135,138],{},[37,136,137],{},"ds_ping_interval"," — seconds between OPTIONS sends. 10s is reasonable for carrier routes; go lower (5s) for critical paths, higher (30s) for backup destinations.",[44,140,141,144],{},[37,142,143],{},"ds_probing_threshold"," — consecutive failed probes before marking destination inactive.",[44,146,147,150],{},[37,148,149],{},"ds_inactive_threshold"," — consecutive successful probes before marking an inactive destination active again.",[22,152,154],{"id":153},"handling-probe-responses-in-kamailiocfg","Handling probe responses in kamailio.cfg",[15,156,157,158,161],{},"The dispatcher module needs an ",[37,159,160],{},"onreply_route"," to process OPTIONS 200 OK responses:",[30,163,166],{"className":164,"code":165,"language":35},[33],"onreply_route[MANAGE_REPLY] {\n    if (status =~ \"[12][0-9][0-9]\") {\n        ds_mark_dst(\"a\"); # mark as active\n    }\n}\n\nfailure_route[MANAGE_FAILURE] {\n    if (t_is_canceled()) {\n        exit;\n    }\n    ds_mark_dst(\"i\"); # mark as inactive\n    if (!ds_next_dst()) {\n        # no more destinations — send 503\n        send_reply(\"503\", \"Service Unavailable\");\n        exit;\n    }\n    t_relay();\n}\n",[37,167,165],{"__ignoreMap":39},[15,169,170,173],{},[37,171,172],{},"ds_next_dst()"," moves to the next destination in the set and returns false when the set is exhausted. This gives you sequential failover within a single INVITE attempt.",[22,175,177],{"id":176},"failover-priority-ordering","Failover priority ordering",[15,179,180],{},"When you have a primary carrier and a backup, use separate setids with explicit fallback logic:",[30,182,185],{"className":183,"code":184,"language":35},[33],"route[CARRIER_ROUTE] {\n    # Try setid 1 (primary carriers) first\n    if (!ds_select_dst(1, 4)) {\n        # setid 1 completely inactive — fall to backup\n        if (!ds_select_dst(2, 0)) {\n            send_reply(\"503\", \"No route available\");\n            exit;\n        }\n    }\n    route(RELAY);\n}\n",[37,186,184],{"__ignoreMap":39},[15,188,189,190,192,193,195],{},"Set ",[37,191,71],{}," (algorithm ",[37,194,63],{}," = hash over call-id) acts as a stable backup. This setup means: try the primary pool first, and only if every destination in it is probing-inactive, fall through to the backup.",[22,197,199],{"id":198},"keepalive-tuning","Keepalive tuning",[15,201,202,203,205],{},"The right ",[37,204,137],{}," depends on your NAT\u002Ffirewall environment and your SLA:",[207,208,209,225],"table",{},[210,211,212],"thead",{},[213,214,215,219,222],"tr",{},[216,217,218],"th",{},"Environment",[216,220,221],{},"Recommended interval",[216,223,224],{},"Reasoning",[226,227,228,240,251,262],"tbody",{},[213,229,230,234,237],{},[231,232,233],"td",{},"Carrier interconnect (no NAT)",[231,235,236],{},"30s",[231,238,239],{},"Low keepalive cost, carrier routes are stable",[213,241,242,245,248],{},[231,243,244],{},"Enterprise trunks (behind FW)",[231,246,247],{},"10s",[231,249,250],{},"Firewall state tables time out in 30-60s",[213,252,253,256,259],{},[231,254,255],{},"WebRTC gateway",[231,257,258],{},"5s",[231,260,261],{},"Fast detection matters for UX",[213,263,264,267,270],{},[231,265,266],{},"Backup\u002Ffailover only",[231,268,269],{},"60s",[231,271,272],{},"Just need to know it's alive",[15,274,275,276,278],{},"Don't set ",[37,277,137],{}," below 5s unless you have a very specific reason — you'll saturate the OPTIONS handling on busy carrier SBCs.",[22,280,282],{"id":281},"monitoring-dispatcher-state","Monitoring dispatcher state",[15,284,285],{},"Check current state via the Kamailio management interface:",[30,287,289],{"className":88,"code":288,"language":90,"meta":39,"style":39},"# All destinations in all sets\nkamcmd dispatcher.list\n\n# Specific set\nkamcmd dispatcher.list 1\n",[37,290,291,297,305,312,318],{"__ignoreMap":39},[94,292,293],{"class":96,"line":97},[94,294,296],{"class":295},"sJ8bj","# All destinations in all sets\n",[94,298,300,302],{"class":96,"line":299},2,[94,301,101],{"class":100},[94,303,304],{"class":104}," dispatcher.list\n",[94,306,308],{"class":96,"line":307},3,[94,309,311],{"emptyLinePlaceholder":310},true,"\n",[94,313,315],{"class":96,"line":314},4,[94,316,317],{"class":295},"# Specific set\n",[94,319,321,323,326],{"class":96,"line":320},5,[94,322,101],{"class":100},[94,324,325],{"class":104}," dispatcher.list",[94,327,329],{"class":328},"sj4cs"," 1\n",[15,331,332],{},"Output shows each destination with its current flags. Automate this into your monitoring stack:",[30,334,336],{"className":88,"code":335,"language":90,"meta":39,"style":39},"# Prometheus textfile exporter (simplified)\nkamcmd dispatcher.list | awk '\u002F^{\u002F { next } \u002FURI:\u002F { uri=$2 } \u002FFlags:\u002F { flags=$2 } \u002FLatency:\u002F { print \"kamailio_dispatcher_latency{uri=\\\"\" uri \"\\\"} \" $2 }' > \u002Fvar\u002Flib\u002Fnode_exporter\u002Fkamailio_dispatcher.prom\n",[37,337,338,343],{"__ignoreMap":39},[94,339,340],{"class":96,"line":97},[94,341,342],{"class":295},"# Prometheus textfile exporter (simplified)\n",[94,344,345,347,349,353,356,359,362],{"class":96,"line":299},[94,346,101],{"class":100},[94,348,325],{"class":104},[94,350,352],{"class":351},"szBVR"," |",[94,354,355],{"class":100}," awk",[94,357,358],{"class":104}," '\u002F^{\u002F { next } \u002FURI:\u002F { uri=$2 } \u002FFlags:\u002F { flags=$2 } \u002FLatency:\u002F { print \"kamailio_dispatcher_latency{uri=\\\"\" uri \"\\\"} \" $2 }'",[94,360,361],{"class":351}," >",[94,363,364],{"class":104}," \u002Fvar\u002Flib\u002Fnode_exporter\u002Fkamailio_dispatcher.prom\n",[15,366,367],{},"With this in place, Grafana can alert when a destination's latency climbs above threshold before it drops completely — giving you early warning rather than reactive failover.",[22,369,371],{"id":370},"putting-it-together","Putting it together",[15,373,374],{},"A production-ready dispatcher setup has:",[376,377,378,381,384,393,396],"ol",{},[44,379,380],{},"dispatcher.list with setids, flags, and explicit priorities",[44,382,383],{},"Active probing with tuned threshold values",[44,385,386,389,390,392],{},[37,387,388],{},"failure_route"," that calls ",[37,391,172],{}," for sequential failover",[44,394,395],{},"Separate setids for primary and backup pools",[44,397,398],{},"Monitoring that surfaces per-destination state and latency",[15,400,401],{},"The dispatcher module is one of Kamailio's most reliable components — we've seen deployments handle 50k+ concurrent sessions with dispatcher failover completing in under 2 seconds on a single-node failure. The key is tuning the probing parameters to match your actual carrier SLA and your tolerance for misdirected calls during the detection window.",[403,404,405],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":39,"searchDepth":299,"depth":299,"links":407},[408,409,410,411,412,413,414],{"id":24,"depth":299,"text":25},{"id":108,"depth":299,"text":109},{"id":153,"depth":299,"text":154},{"id":176,"depth":299,"text":177},{"id":198,"depth":299,"text":199},{"id":281,"depth":299,"text":282},{"id":370,"depth":299,"text":371},"SIP","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1558494949-ef010cbdcc31?w=1200&q=80","2025-11-15","How to configure Kamailio's dispatcher module for production-grade failover: dispatcher.list syntax, probing modes, failover priorities, and keepalive tuning.","md",{},"\u002Fblog\u002Fkamailio-dispatcher-failover",8,{"title":5,"description":418},"blog\u002Fkamailio-dispatcher-failover",[426,427,428,429],"kamailio","dispatcher","failover","sip-routing","-l95leN4s_ekVVpHoAppgfpVCoUM5TlYHXmg4uOMmWY",1776974166862]