Blog · · 5 min read

Why Kamailio and FreeSWITCH work so well together

Put Kamailio at the edge for registrations, routing and security, and run FreeSWITCH behind it for anything that touches audio. Each part can then fail, scale and be patched without the other. Here's why that split works, and where it needs care.

By the Kamailio Support engineers.

Who does what

The split follows what each project does best. Kamailio is a SIP proxy and registrar. It handles lots of transactions for very little memory, and it never touches RTP unless you add rtpengine. FreeSWITCH is a back-to-back user agent. It anchors media, so it can play prompts, mix conferences, record and transcode.

  • Kamailio: registrar/usrloc for registrations, auth_db for digest auth, dispatcher for load balancing across media servers, pike and htable for rate limiting and bans, permissions for trunk ACLs, and tm failure routes for retries.
  • FreeSWITCH: IVR menus, mod_conference, mod_voicemail, mod_callcenter queues, call recording with record_session, and transcoding between codecs, all running on mod_sofia profiles that only talk to Kamailio.

A phone registers with Kamailio and never knows FreeSWITCH exists. When a call needs a feature, Kamailio relays it to a FreeSWITCH node. When FreeSWITCH needs to reach a user, it sends the INVITE back through Kamailio, which does the lookup("location").

Why one box doing both hurts

Signalling and media load grow in different ways. A REGISTER refresh or an OPTIONS keepalive costs Kamailio almost nothing. A recorded, transcoded conference leg costs FreeSWITCH real CPU and disk I/O. If one box does both, a busy conference bridge can slow down registrations. Phones then start re-registering, and the extra signalling adds even more load.

Failure works the same way. If an all-in-one server crashes, every phone loses its registration, every call drops and every feature is gone at once. Users see phones showing "No service", and the helpdesk phones are down too. With the split, losing a media node takes out only the calls on that node.

Upgrades are the third reason. FreeSWITCH releases and codec modules change more often than your edge proxy should. With the two separated, you can patch the media tier one node at a time while registrations and routing carry on untouched.

Failover that callers actually notice

Be honest about what happens. If a FreeSWITCH node dies, the calls it was anchoring are lost. Kamailio can't rescue RTP it never handled. Those callers hear the call drop. When they redial, the call lands on a healthy node and the IVR, queue or voicemail answers as normal. Phones stay registered the whole time, because registrations live in Kamailio.

For that to work, Kamailio has to notice a dead node quickly and move to the next one within the same transaction. dispatcher sends OPTIONS probes to each node and marks unresponsive ones inactive. A failure route catches the calls that were already in flight to the node when it died:

kamailio.cfg
# 2 = failover support: keep the other destinations for ds_next_dst()
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_probing_threshold", 2)

route[TO_MEDIA] {
    if (!ds_select_dst("1", "4")) {
        send_reply("503", "No media servers available");
        exit;
    }
    # 2s for a media node to send any reply, 120s once it's ringing
    t_set_fr(120000, 2000);
    t_on_failure("MEDIA_FAIL");
    route(RELAY);
}

failure_route[MEDIA_FAIL] {
    if (t_is_canceled()) exit;
    if (t_check_status("5[0-9][0-9]") || (t_branch_timeout() && !t_branch_replied())) {
        # no reply at all: take the node out until probing brings it back.
        # a 5xx only moves this call on (a 503 may just mean "busy right now").
        if (t_branch_timeout() && !t_branch_replied()) {
            ds_mark_dst("ip");
        }
        if (ds_next_dst()) {
            t_set_fr(120000, 2000);
            t_on_failure("MEDIA_FAIL");
            route(RELAY);
        }
    }
}

The 2-second fr_timer suits media servers on your own network. A healthy FreeSWITCH sends 100 Trying almost immediately, so callers shouldn't sit through the 30-second default before a retry. If failover isn't happening as you expect, work through our dispatcher failover guide.

Keep related calls on the same media server

Round-robin works for calls that stand alone. Some features need several calls on the same box. Every leg of a conference has to reach the FreeSWITCH node hosting that room, otherwise you end up with two half-empty conferences that share a name. Use a hashing algorithm for those number ranges, for example ds_select_dst("2", "3") to hash over the Request-URI. Every dial-in to the same conference number then reaches the same node while it stays up.

Voicemail needs shared state instead. Picture a hosted PBX with four media nodes. A message left on node A has to be playable when the user dials in and lands on node C. That means putting the voicemail storage and mod_voicemail's database on something every node can reach. It also means routing MWI NOTIFYs back through Kamailio, so the lamp on the handset reaches the phone's registered contact.

Security belongs at the edge

Kamailio is the only thing the internet should see. Digest auth, pike flood detection, User-Agent filtering and htable bans all happen there, before any media resources are used. Our friendly-scanner guide covers the blocking rules.

On the FreeSWITCH side, firewall SIP so only the Kamailio nodes can reach it, and set apply-inbound-acl on the Sofia profile to an ACL listing those addresses. Calls from the proxy then come in already authenticated. Kamailio can pass the tenant or authenticated user as an X- header, which the dialplan reads as ${sip_h_X-Tenant}, so FreeSWITCH never has to repeat the auth work. Callers never see any of this. All they notice is that toll-fraud attempts and scanner floods don't slow down their calls.

What the ops team gets

Maintenance becomes routine instead of an out-of-hours event. To patch a media node, mark it inactive with kamcmd dispatcher.set_state i 1 sip:10.0.0.12:5060, wait for its active channel count in fs_cli to drain to zero, then upgrade and reactivate it. New calls never touch it and nobody gets cut off. Adding capacity means adding a row to the dispatcher list and running kamcmd dispatcher.reload.

You also get clearer signals when something goes wrong. Registration counts, dispatcher destination states and FreeSWITCH channel counts are separate metrics that point to separate layers. "Phones can't register" and "IVR prompts are silent" stop looking like the same incident. Both tiers have their own failure modes: NAT and SDP on the Kamailio side, and codec and RTP port ranges on FreeSWITCH. If you want a second pair of eyes on a FreeSWITCH media tier, our FreeSWITCH consultancy covers exactly that.

Comments

No comments yet.

Add a comment

One of our engineers reads every comment before it appears. Please don't post passwords, IP addresses or other private details from your platform.

We publish your name and comment with this post. To have a comment removed, email [email protected].

Work with us

Want this on your platform?

Three senior engineers who've run Kamailio and FreeSWITCH in production since 2005. You talk to the person doing the work, and we'll sign your NDA before you share anything sensitive. See pricing, or read about our Kamailio support.

Call us Email