If you've ever wired a phone system into an app, you've probably run into the term SIP trunk and wondered what's really happening under the hood. This guide walks through it from a developer's point of view: what a trunk is, how a call flows, and the gotchas that bite people in production.
What is a SIP trunk?
A SIP trunk is a virtual connection between your phone system (a PBX, a softswitch, or your own app) and a carrier that connects you to the public phone network (PSTN).
Instead of physical copper lines, you get:
- Signalling over SIP (Session Initiation Protocol), usually on UDP/TCP 5060 or TLS 5061
- Media (the actual audio) over RTP, on a range of UDP ports
- Phone numbers (DIDs) routed to your system by the carrier
One trunk can carry many simultaneous calls. You're limited by your contract's channel count and your bandwidth, not by how many wires you have.
The life of an outbound call
Here's a simplified flow when your PBX places a call through a trunk:
PBX Carrier
| INVITE (+15551234567) -> |
| <- 407 Proxy Auth Required |
| INVITE (with credentials) ->|
| <- 100 Trying |
| <- 180 Ringing |
| <- 200 OK (SDP answer) |
| ACK -> |
| ======= RTP audio ======== |
| BYE -> |
| <- 200 OK |
A few things worth knowing:
- The 407 is normal. Most carriers challenge the first INVITE with digest authentication. Your SIP stack answers with a hashed response, and the call proceeds.
- SDP is where media is negotiated. The INVITE carries an SDP offer (the IP, port and codecs you can receive on), and the 200 OK carries the carrier's answer.
- Post-dial delay (PDD) is the time from INVITE to the first 180/183. It's the "dead air" the caller hears before ringing, and it's the first metric to watch when quality complaints come in.
Authentication: registration vs. IP-based
Carriers usually support one of two models:
-
Registration-based: your PBX sends a
REGISTERwith a username and password every few minutes, like a desk phone does. It's easy to set up and works behind dynamic IPs. - IP-based (static): the carrier whitelists your public IP, so no registration is needed. It's better for high-volume or multi-server setups, but you need a stable IP.
If you run several servers behind a load balancer, IP-based auth with an SBC (session border controller) in front is usually the cleaner design.
The NAT problem (and why one-way audio happens)
The most common SIP bug isn't in SIP at all. It's NAT.
SIP messages contain IP addresses inside the payload (in the Contact header and the SDP). If your server sits behind NAT and advertises its private IP (10.0.0.5), the carrier will happily send audio to an address it can't reach. The result is a call that connects but where only one side can hear.
Fixes, in order of preference:
- Configure your PBX's external/public address setting so it rewrites SIP and SDP with the public IP
- Put an SBC at the edge that handles NAT traversal for you
- Make sure your RTP port range (e.g. 10000–20000/UDP) is open and forwarded
Codecs: keep it simple
- G.711 (PCMU/PCMA): uncompressed, ~87 kbps per call with overhead. It's universally supported and the safest default for trunks.
- G.729: compressed, ~31 kbps. It saves bandwidth but sometimes needs licensing, and quality drops a little.
- Opus: great quality and adaptive, but many PSTN carriers don't support it on the trunk side.
A good rule is to use Opus between your app and your users, and G.711 between your PBX and the carrier. Let the PBX transcode if needed.
Capacity planning
A rough formula for bandwidth:
concurrent_calls × per_call_bandwidth × 1.2 (headroom)
So 50 concurrent G.711 calls needs about 50 × 87 kbps × 1.2 ≈ 5.2 Mbps symmetric.
Don't forget the non-bandwidth limits too:
-
CPS (calls per second): carriers often cap this, and bursts get rejected with
503 -
Channel limit: exceeding your purchased channels also returns
503or486 -
File descriptors: each call with media needs sockets. On Linux the default
ulimit -n 1024can become your real ceiling long before CPU does.
Failover and redundancy
Production trunks should never be a single point of failure:
- Use DNS SRV records so your PBX can fail over between carrier endpoints automatically
- Configure a secondary trunk (ideally a different carrier) with routing rules that retry on
503/timeout - Send SIP OPTIONS pings to detect a dead trunk before customers do
Security checklist
SIP endpoints exposed to the internet get scanned constantly. At minimum:
- Use TLS (5061) + SRTP where your carrier supports it
- Use strong, unique credentials, never extension-number-as-password
- Run fail2ban or equivalent on SIP auth failures
- Set geo/destination restrictions on outbound calling to block toll fraud to premium international numbers
- Set spending limits with your carrier
Wrapping up
SIP trunking isn't magic. It's signalling plus RTP plus a carrier on the other end. Most production issues come down to NAT, capacity limits or missing failover, not the protocol itself.
If you'd rather not run the carrier side yourself, managed providers handle routing, failover and number porting for you. We work on this at Salamtalk, where we run SIP trunking and cloud PBX infrastructure for growing teams. Happy to answer questions in the comments.
What's the weirdest SIP bug you've debugged? One-way audio stories welcome. 👇
Top comments (0)