If you’ve been working with Asterisk in production for a while, sooner or later you’ve run into the same problem: a single Asterisk server doesn’t scale well. Calls pile up, CPU spikes with transcoding, and any restart to apply a configuration change means cutting the service. The classic solution is to run several Asterisk servers in parallel and put something in front to distribute the load. That “something” is Kamailio, and the tool that makes it possible is the dispatcher module.
This tutorial shows you how to install Kamailio on Debian/Ubuntu, configure the dispatcher module and route SIP calls to different Asterisk servers based on the originating user. It’s not magic — it’s well-configured SIP routing logic.
The architecture we’re going to build
- Kamailio acts as the SIP proxy. Endpoints (softphones, IP phones, SIP trunks) register against Kamailio, and Kamailio decides which Asterisk to send each call to.
- Asterisk A (192.168.1.10) and Asterisk B (192.168.1.20) are the media servers. This is where the business logic lives: IVRs, queues, extensions, etc.
- Kamailio does not touch the audio. It is a pure signalling proxy. RTP goes directly between the endpoint and Asterisk.
Kamailio’s dispatcher module maintains a set of destinations (the Asterisk servers) and distributes them according to different algorithms: round-robin, user hash, active load… In our case we’ll use user hash (algorithm 4), which guarantees that the same user always goes to the same Asterisk as long as both are available. This is important so that channel state, call parking and pickup groups remain consistent.
Prerequisites
- A clean Debian 12 or Ubuntu 22.04/24.04 server for Kamailio
- Two working Asterisk servers (or at least reachable over the network)
- Root or sudo access on all servers
- UDP port 5060 open between Kamailio and the Asterisk servers
1. Installing Kamailio
Use the official Kamailio repositories. Do not install the package from the base Debian/Ubuntu repository — it’s usually way behind on version.
# Add the official Kamailio repository (version 5.8)
curl -fsSL https://deb.kamailio.org/kamailiodebkey.gpg | gpg --dearmor -o /usr/share/keyrings/kamailio.gpg
echo "deb [signed-by=/usr/share/keyrings/kamailio.gpg] http://deb.kamailio.org/kamailio58 $(lsb_release -cs) main" \
> /etc/apt/sources.list.d/kamailio.list
apt update
apt install -y kamailio kamailio-mysql-modules kamailio-utils-modulesIn addition to the base modules, we install kamailio-utils-modules because it includes the dispatcher module, the star of this tutorial.
Check that Kamailio starts up:
systemctl enable kamailio
systemctl start kamailio
systemctl status kamailio2. Basic Kamailio configuration
The main configuration file is /etc/kamailio/kamailio.cfg. Before editing it, adjust the global parameters in /etc/default/kamailio:
MEMORY=128
SHM_MEMORY=256
CFGFILE=/etc/kamailio/kamailio.cfg
USER=kamailio
GROUP=kamailio
DUMP_CORE=noNow replace the contents of /etc/kamailio/kamailio.cfg with the following:
#!KAMAILIO
##############################################
# Global parameters
##############################################
debug=2
log_stderror=no
log_facility=LOG_LOCAL0
fork=yes
children=4
port=5060
listen=udp:0.0.0.0:5060
##############################################
# Modules to load
##############################################
loadmodule "tm.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "dispatcher.so"
##############################################
# Module parameters
##############################################
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "fr_timer", 30000)
modparam("tm", "fr_inv_timer", 120000)
modparam("rr", "enable_full_lr", 1)
# dispatcher - the key module
modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "dst_avp", "$avp(ds_dst)")
modparam("dispatcher", "grp_avp", "$avp(ds_grp)")
modparam("dispatcher", "cnt_avp", "$avp(ds_cnt)")
modparam("dispatcher", "sock_avp", "$avp(ds_sock)")
# Health check: probe Asterisk servers every 30 seconds
modparam("dispatcher", "ds_ping_interval", 30)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_ping_from", "sip:kamailio@yourdomain.com")
##############################################
# Routing logic
##############################################
request_route {
if (!mf_process_maxfwd_header(10)) {
sl_send_reply("483", "Too Many Hops");
exit;
}
if (!sanity_check()) {
exit;
}
if (is_method("ACK")) {
if (t_check_trans()) { t_relay(); }
exit;
}
if (is_method("CANCEL")) {
if (t_check_trans()) { t_relay(); }
exit;
}
if (is_method("INVITE|SUBSCRIBE")) {
record_route();
}
if (loose_route()) {
route(RELAY);
exit;
}
if (is_method("INVITE")) {
route(DISPATCH);
exit;
}
route(RELAY);
}
route[DISPATCH] {
# User hash (algorithm 4): same user always goes to the same Asterisk
if (!ds_select_dst(1, 4)) {
xlog("L_ERR", "No destinations available in group 1\n");
sl_send_reply("503", "Service Unavailable");
exit;
}
xlog("L_INFO", "Routing call from $fu to $du via dispatcher\n");
t_on_failure("MANAGE_FAILURE");
route(RELAY);
}
route[RELAY] {
if (!t_relay()) {
sl_reply_error();
}
exit;
}
failure_route[MANAGE_FAILURE] {
if (t_is_canceled()) { exit; }
if (t_check_status("408|5[0-9][0-9]|6[0-9][0-9]")) {
ds_mark_dst("ip");
if (!ds_next_dst()) {
xlog("L_ERR", "No more destinations. Failing call.\n");
t_reply("503", "All destinations failed");
exit;
}
route(RELAY);
}
}3. The dispatcher.list file: where you define your Asterisk servers
This is the most important file for load balancing. Create /etc/kamailio/dispatcher.list:
# /etc/kamailio/dispatcher.list
# Format: setid uri flags priority attrs description
1 sip:192.168.1.10:5060 0 0 weight=50 Asterisk-A
1 sip:192.168.1.20:5060 0 0 weight=50 Asterisk-BThe group number (1) is what you pass to ds_select_dst(1, 4). You can have multiple groups for different purposes: one for inbound calls, one for outbound, one for premium users, etc.
Algorithm 4 hashes the Request-URI (the called user) — same destination always lands on the same Asterisk. Algorithm 7 hashes the Call-ID (more uniform distribution). Algorithm 0 is plain round-robin.
After modifying the file, reload without restarting Kamailio:
kamcmd dispatcher.reload4. Configuring Asterisk to accept calls from Kamailio
In PJSIP (/etc/asterisk/pjsip.conf), create an endpoint for Kamailio:
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0:5060
[kamailio]
type=endpoint
context=from-kamailio
disallow=all
allow=alaw,ulaw
direct_media=no
trust_id_inbound=yes
from_domain=yourdomain.com
[kamailio]
type=identify
endpoint=kamailio
match=192.168.1.1 ; Your Kamailio IP
[kamailio]
type=aor
max_contacts=1The direct_media=no parameter is critical. It tells Asterisk not to negotiate direct media between endpoints, since Kamailio won’t be in the RTP path. Set it to yes with NATted clients and audio will break.
In the dialplan (/etc/asterisk/extensions.conf):
[from-kamailio]
exten => _.,1,NoOp(Call received from Kamailio for EXTEN_PLACEHOLDER)
same => n,Dial(PJSIP/EXTEN_PLACEHOLDER,30)
same => n,Hangup()(Replace EXTEN_PLACEHOLDER with ${EXTEN} in the actual file.)
5. Verifying the dispatcher is working
kamcmd dispatcher.listSample output:
DISPATCHER SETS[1]:
SET[0]:
ID: 1
DESTS[2]:
DEST[0]: URI=sip:192.168.1.10:5060 FLAGS=AP PRIORITY=0
DEST[1]: URI=sip:192.168.1.20:5060 FLAGS=AP PRIORITY=0The FLAGS field tells you everything:
- A (Active): destination is up and receiving calls
- P (Probing): Kamailio is sending periodic OPTIONS pings
- I (Inactive): destination is not responding, marked as down
- D (Disabled): manually disabled
To put an Asterisk server into maintenance without dropping live calls:
# Disable
kamcmd dispatcher.set_state ip 1 sip:192.168.1.20:5060
# Re-enable
kamcmd dispatcher.set_state ap 1 sip:192.168.1.20:50606. Debugging and logs
tail -f /var/log/syslog | grep kamailioFor raw SIP inspection, sngrep is your best friend:
apt install -y sngrep
sngrep -d eth0 port 5060Adjust the debug level on the fly (no restart needed):
kamcmd cfg.set_now_int core debug 4 # verbose
kamcmd cfg.set_now_int core debug 2 # back to normalConclusions and next steps
With this setup you have a functional SIP proxy that load balances calls across multiple Asterisk servers using user hash, with automatic failure detection and transparent failover. It’s the foundation for a serious telephony architecture.
What’s not covered here but is the natural next step:
- User registration:
registrarmodule + MySQL database so endpoints register against Kamailio. - Authentication:
authandauth_dbmodules for endpoint authentication. - NAT traversal:
nathelper+ RTPengine or rtpproxy for clients behind NAT. - TLS and SRTP: encrypt both signalling and audio.
The dispatcher is just one of Kamailio’s many modules, but it’s the one that gives you the most leverage in a multi-Asterisk environment. Master it, and you’ll have the base to build whatever you need on top.




¡Inicia la conversación!
Sé el primero en compartir tu opinión. Tu comentario puede ayudar a otros.