[{"data":1,"prerenderedAt":418},["ShallowReactive",2],{"post-\u002Fblog\u002Fsbc-sizing-guide":3},{"id":4,"title":5,"author":6,"body":7,"category":402,"coverImage":403,"date":404,"description":405,"extension":406,"meta":407,"navigation":328,"path":408,"readingTime":409,"seo":410,"stem":411,"tags":412,"__hash__":417},"posts\u002Fblog\u002Fsbc-sizing-guide.md","Sizing your SBC: a practical capacity planning guide","Tumarm Engineering",{"type":8,"value":9,"toc":391},"minimark",[10,14,18,21,26,29,36,42,45,49,52,120,123,136,140,143,158,220,223,238,242,248,253,261,266,280,283,289,293,296,307,310,314,317,361,365,368,388],[11,12,5],"h1",{"id":13},"sizing-your-sbc-a-practical-capacity-planning-guide",[15,16,17],"p",{},"Most SBC sizing exercises start with the vendor's datasheet and end with a box that's either oversized by 4x or hits CPU at 70% of projected load. The datasheet numbers assume G.711 calls, no transcoding, and a pristine network — none of which describe real production traffic.",[15,19,20],{},"Here's how to size an open-source SBC (Kamailio + rtpengine) for the traffic you'll actually carry.",[22,23,25],"h2",{"id":24},"two-separate-problems","Two separate problems",[15,27,28],{},"SBC capacity splits cleanly into two planes:",[15,30,31,35],{},[32,33,34],"strong",{},"Signaling plane"," (Kamailio) — handles SIP message processing: INVITE, REGISTER, OPTIONS, BYE, and all the state machine around them. Measured in calls per second (CPS) and concurrent dialogs.",[15,37,38,41],{},[32,39,40],{},"Media plane"," (rtpengine) — handles RTP\u002FSRTP forwarding, transcoding, and recording. Measured in concurrent sessions and CPU cores for transcoding.",[15,43,44],{},"Size them independently and deploy them on separate machines. A signaling overload shouldn't kill media, and a transcoding spike shouldn't drop your SIP registrations.",[22,46,48],{"id":47},"signaling-sizing-kamailio","Signaling sizing (Kamailio)",[15,50,51],{},"Kamailio's CPS capacity on modern hardware:",[53,54,55,74],"table",{},[56,57,58],"thead",{},[59,60,61,65,68,71],"tr",{},[62,63,64],"th",{},"Hardware",[62,66,67],{},"INVITE CPS (no DB)",[62,69,70],{},"INVITE CPS (with Postgres)",[62,72,73],{},"Concurrent dialogs",[75,76,77,92,106],"tbody",{},[59,78,79,83,86,89],{},[80,81,82],"td",{},"4-core 2020-era Xeon",[80,84,85],{},"4,000–6,000",[80,87,88],{},"1,500–2,500",[80,90,91],{},"200,000+",[59,93,94,97,100,103],{},[80,95,96],{},"8-core Xeon\u002FEPYC",[80,98,99],{},"10,000–15,000",[80,101,102],{},"4,000–7,000",[80,104,105],{},"500,000+",[59,107,108,111,114,117],{},[80,109,110],{},"16-core EPYC",[80,112,113],{},"20,000–30,000",[80,115,116],{},"8,000–15,000",[80,118,119],{},"1M+",[15,121,122],{},"Rules of thumb:",[124,125,126,130,133],"ul",{},[127,128,129],"li",{},"If your routing decisions hit a database per INVITE, capacity drops by 50-70%. Use htable or Redis for hot lookups.",[127,131,132],{},"REGISTER storms (after a network partition, for example) can generate 10-50x your normal CPS. Size for the storm, not steady state.",[127,134,135],{},"Concurrent dialogs consume memory linearly. At ~1KB per dialog, 500k dialogs = 500MB. This is rarely the bottleneck.",[22,137,139],{"id":138},"media-sizing-rtpengine","Media sizing (rtpengine)",[15,141,142],{},"rtpengine's media capacity is determined by:",[144,145,146,152],"ol",{},[127,147,148,151],{},[32,149,150],{},"Concurrent sessions"," — RTP forwarding is cheap. A single 8-core machine handles 50,000+ concurrent G.711 relay sessions without transcoding.",[127,153,154,157],{},[32,155,156],{},"Transcoding overhead"," — this is where you actually consume CPU. Rough per-core capacity:",[53,159,160,170],{},[56,161,162],{},[59,163,164,167],{},[62,165,166],{},"Codec pair",[62,168,169],{},"Concurrent sessions per core",[75,171,172,180,188,196,204,212],{},[59,173,174,177],{},[80,175,176],{},"G.711 relay (no transcode)",[80,178,179],{},"~10,000",[59,181,182,185],{},[80,183,184],{},"G.711 μ-law ↔ a-law",[80,186,187],{},"~5,000",[59,189,190,193],{},[80,191,192],{},"G.711 ↔ G.729 (software)",[80,194,195],{},"~150–200",[59,197,198,201],{},[80,199,200],{},"G.711 ↔ Opus",[80,202,203],{},"~80–120",[59,205,206,209],{},[80,207,208],{},"G.711 ↔ AMR-WB",[80,210,211],{},"~60–100",[59,213,214,217],{},[80,215,216],{},"G.729 ↔ Opus",[80,218,219],{},"~50–80",[15,221,222],{},"G.729 transcoding is the one that catches teams by surprise. If 30% of your calls transcode G.711 to G.729, you need 15x the CPU compared to relay-only.",[144,224,226,232],{"start":225},3,[127,227,228,231],{},[32,229,230],{},"SRTP overhead"," — SRTP encryption\u002Fdecryption costs roughly 5-10% additional CPU vs. plain RTP. Negligible unless you're doing full transcoding at the same time.",[127,233,234,237],{},[32,235,236],{},"Recording"," — writing PCM to disk while relaying adds I\u002FO load. Budget 2-3x the RTP bandwidth in disk write throughput, and use dedicated disks or object store offload.",[22,239,241],{"id":240},"working-through-an-example","Working through an example",[15,243,244,247],{},[32,245,246],{},"Scenario:"," 500 concurrent calls, 30% G.711↔G.729 transcoding, 70% G.711 relay, SRTP on all legs, no recording.",[15,249,250],{},[32,251,252],{},"Signaling (Kamailio):",[124,254,255,258],{},[127,256,257],{},"At 500 concurrent calls with ~3-minute average duration: ~2.8 CPS. A basic 4-core Kamailio handles this comfortably — signaling is not the bottleneck.",[127,259,260],{},"Size for 10x: 28 CPS for spikes. Still well within a 4-core node.",[15,262,263],{},[32,264,265],{},"Media (rtpengine):",[124,267,268,271,274,277],{},[127,269,270],{},"350 G.711 relay sessions: negligible",[127,272,273],{},"150 G.711↔G.729 sessions at ~175 sessions\u002Fcore = 0.86 cores",[127,275,276],{},"SRTP overhead: +10% = ~0.95 cores total for transcoding",[127,278,279],{},"Add 50% headroom: 1.5 cores for transcoding",[15,281,282],{},"Result: a 4-core rtpengine server handles this comfortably with room for 3x growth.",[15,284,285,288],{},[32,286,287],{},"Revised scenario with recording:","\nAdd recording for all 500 calls at G.711 = 64kbps per leg × 2 legs = 128kbps per call. 500 calls = 64MB\u002Fs of disk writes. This requires dedicated disk I\u002FO — don't share with OS or application disks.",[22,290,292],{"id":291},"network-sizing","Network sizing",[15,294,295],{},"RTP bandwidth per call:",[124,297,298,301,304],{},[127,299,300],{},"G.711 (20ms ptime): ~80kbps per direction",[127,302,303],{},"G.729 (20ms ptime): ~26kbps per direction",[127,305,306],{},"Opus (20ms ptime, default): ~40kbps per direction",[15,308,309],{},"For 500 concurrent G.711 calls (both directions through rtpengine): 500 × 2 × 80kbps = 80Mbps. A gigabit NIC handles this with headroom. Above 5,000 concurrent G.711 calls, start thinking about 10GbE and multiple NIC queues.",[22,311,313],{"id":312},"hardware-selection-checklist","Hardware selection checklist",[15,315,316],{},"Before finalizing hardware:",[124,318,321,331,337,343,349,355],{"className":319},[320],"contains-task-list",[127,322,325,330],{"className":323},[324],"task-list-item",[326,327],"input",{"disabled":328,"type":329},true,"checkbox"," Calculate peak CPS including registration storms (not just steady-state call setup)",[127,332,334,336],{"className":333},[324],[326,335],{"disabled":328,"type":329}," Break down your codec mix — G.711 relay vs. transcoding matters by 50-100x",[127,338,340,342],{"className":339},[324],[326,341],{"disabled":328,"type":329}," Add 50% headroom minimum on both planes for traffic growth and failure scenarios",[127,344,346,348],{"className":345},[324],[326,347],{"disabled":328,"type":329}," If recording, isolate disk I\u002FO on dedicated volumes or offload to S3",[127,350,352,354],{"className":351},[324],[326,353],{"disabled":328,"type":329}," Plan HA pairs — your sizing for a single node assumes it's always up",[127,356,358,360],{"className":357},[324],[326,359],{"disabled":328,"type":329}," Test under load with SIPp before production. The numbers above are empirical, not guaranteed.",[22,362,364],{"id":363},"ha-pairing","HA pairing",[15,366,367],{},"Deploy Kamailio and rtpengine in HA pairs with keepalived for IP failover. The specific failure modes to test:",[124,369,370,376,382],{},[127,371,372,375],{},[32,373,374],{},"Kamailio node failure",": the peer takes the VIP within \u003C2s. In-progress calls can survive if you're using stateless processing (most INVITE routing is).",[127,377,378,381],{},[32,379,380],{},"rtpengine node failure",": in-progress calls drop at the media layer. Signal-level REINVITE is required to re-establish media. Design for this or deploy an rtpengine cluster where the partner node can take over an existing session via the ng control protocol.",[127,383,384,387],{},[32,385,386],{},"Database failure",": if your routing tables are in Postgres, a DB failure can take down both signaling nodes. Use htable caching with a warm-reload on DB reconnect.",[15,389,390],{},"Sizing is easier when you've measured. Instrument your deployment with Prometheus + Grafana from day one — knowing your actual CPS, concurrent session count, and codec mix takes all the guesswork out of capacity planning for the next upgrade.",{"title":392,"searchDepth":393,"depth":393,"links":394},"",2,[395,396,397,398,399,400,401],{"id":24,"depth":393,"text":25},{"id":47,"depth":393,"text":48},{"id":138,"depth":393,"text":139},{"id":240,"depth":393,"text":241},{"id":291,"depth":393,"text":292},{"id":312,"depth":393,"text":313},{"id":363,"depth":393,"text":364},"SBC","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1544197150-b99a580bb7a8?w=1200&q=80","2025-10-28","How to calculate concurrent sessions, CPS limits, transcoding overhead, and hardware sizing for a Session Border Controller deployment.","md",{},"\u002Fblog\u002Fsbc-sizing-guide",10,{"title":5,"description":405},"blog\u002Fsbc-sizing-guide",[413,414,415,416],"sbc","capacity-planning","rtpengine","kamailio","y_UhHc56IVuqHPZBfiWnBkRd57fCUssc1W6n76VMEFM",1776974166862]