Business phone numbers and phone menus for the United States and Canada. FRES(510) 288-6288[email protected]Sign in

Home › Open-source VoIP › OpenSIPS

Open-source VoIP

OpenSIPS in depth: the SIP server, its modules and a working load balancer.

OpenSIPS is not a phone system. It is a programmable SIP router that sits in front of phone systems, carriers and SBC estates and decides where every request goes. This guide explains how it works from the project’s own documentation and source, says which release to run today, and walks through a complete configuration that spreads inbound calls across two media servers.

Last checked 26 October 2026 · 50 cited sources · 24 min read

A SIP load balancer

What OpenSIPS is, and what it is not

OpenSIPS describes itself as "an Open Source SIP proxy/server for voice, video, IM, presence and any other SIP extensions".2 Its own feature list is longer than that sentence suggests: a SIP registrar, a router or proxy, a redirect server, a presence agent, a back-to-back user agent, an IM server, a load balancer or dispatcher, a "SIP front end for gateways/asterisk", a NAT traversal unit and an application server.1 It is written in C and published under the GNU GPL version 2.56

The useful mental model is the one in our open-source VoIP overview: OpenSIPS works on the signalling layer. It receives SIP requests, decides what to do with them according to a script you write, and forwards, answers or rejects them. It does not play prompts, record calls or run a voicemail box. Those jobs belong to a media server or PBX such as Asterisk or FreeSWITCH, and in most deployments OpenSIPS stands in front of several of them.

Audio is the one area where that line has blurred. Traditionally OpenSIPS steers media through an external relay (RTPProxy or Sipwise rtpengine) that it controls over a small protocol.31 Release 3.6 added rtp.io, which performs "RTP relaying and processing directly inside the OpenSIPS process", removing the need for an external relay in some designs.10

Our view: think of OpenSIPS as a router with a scripting language, not as a product. Nothing happens unless the script says so. That is what makes it fast and flexible, and it is also why a configuration copied from a forum without understanding it is the most common way to end up with an open relay or a toll-fraud bill.

Where it came from: SER, OpenSER, then two projects

OpenSIPS and Kamailio share an ancestor, and the timeline explains why their configuration files still look alike.

  • 2001 to 2002, SER. The SIP Express Router project was started on 2 September 2001 by the FhG Fokus research institute in Berlin, and released as open source under the GPL in autumn 2002.7 OpenSIPS’s licence file still says that "OpenSIPS software is derived from SER (SIP Express Router) project developed by FhG FOKUS."6
  • June 2005, OpenSER. "OpenSER project started on the 14th June, 2005."7
  • July and August 2008, the split. On 28 July 2008 OpenSER was renamed Kamailio "due to trademark issues".7 A week later, on 4 August 2008, Voice System, the company that had been OpenSER’s main contributor, announced OpenSIPS together with its first release, 1.4.0. The announcement said OpenSIPS "is a continuation of the OpenSER project" and cited delayed releases, slipping quality control and a lack of project coordination as the reasons. Its summary diagram was simply OpenSER branching into Kamailio and OpenSIPS.8 The versions page confirms 1.4.x as the first OpenSIPS release, dated 4 August 2008.3
  • 2010 to 2012, Kamailio and SER reunite. Kamailio 3.0.0, released on 11 January 2010, was the first version built from a single source tree with SER, and the integration was declared complete on 20 December 2012.7

From 2008 onwards the two projects developed separately. The script syntax, the module names (tm, rr, dispatcher, usrloc) and the general shape of a routing script are recognisably shared. Function signatures, parameters and whole subsystems are not.

Practical consequence

An OpenSIPS answer is not a Kamailio answer, and the reverse. A dispatcher example written for one will often load in the other's parser and then misbehave, or fail on a function that only one of them has. Always check the documentation for the exact project and branch you run. OpenSIPS publishes a separate manual for each release (3.6 and 4.0 below), and the module pages say which version a behaviour arrived in.

Architecture: processes, memory, transactions and dialogs

Processes and memory

OpenSIPS is a multi-process server. For UDP, each listening interface gets its own pool of worker processes: udp_workers sets the number "for each UDP or SCTP interface" and defaults to 8, and a single socket line can override it.12 TCP-based transports (TCP, TLS, WebSocket) have their own tcp_workers pool, also defaulting to 8, and there are dedicated timer processes (timer_workers, default 1).12 Any of these pools can be governed by an auto_scaling_profile that forks or terminates processes depending on load.12

Release 4.0 changed the TCP side substantially. Before 4.0, a TCP main process handed connections to TCP worker processes, so "connections had to be passed back and forth between the TCP Main process and the TCP workers". In 4.0, one dedicated process owns every TCP and TLS connection and uses threads for socket input and output. Complete SIP messages are passed to the workers, and the workers submit writes back as asynchronous jobs.11 The 4.0 manual describes the new tcp_threads parameter, which defaults to the number of online CPUs.13 The project says the redesign removes the special OpenSSL handling the multi-process model required.9

All processes share one shared-memory pool, sized with the -m command-line option in megabytes.16 The core documentation gives 32 MB as the default shared memory size.12 Each process also has private memory for the message it is working on. The distinction matters: the TM module "copies clones of received SIP messages in shared memory", while "non-TM functions operate over the received message in private memory".17

Stateless, transaction-stateful and dialog-stateful

OpenSIPS can handle a request at three levels of memory, and choosing the level is a design decision you make in the script.

  • Stateless. The SL module answers requests "without keeping state", which the documentation recommends where you "wish not to burden server’s memory and scale well".18
  • Transaction-stateful. TM tracks a request and its replies. t_relay() creates the transaction, absorbs retransmissions from upstream, generates retransmissions downstream and correlates replies to requests. Forking and failover must be stateful. The documentation notes that this costs memory and latency in exchange for not repeating work such as DNS lookups.17
  • Dialog-stateful. The dialog module tracks the whole call, from the INVITE created with create_dialog() to the BYE. If no BYE arrives, default_timeout ends the dialog. Dialog state supports profiles ("how many calls does this customer have up?"), per-call variables and termination initiated by the proxy.23 Several higher-level modules require it, among them load_balancer, fraud_detection and rtp_relay.384332

The routing script

Everything OpenSIPS does is driven by opensips.cfg. The file shipped in the repository has three parts: global parameters, a modules section (loadmodule and modparam lines) and the routing logic.30 The routing logic is a set of route blocks, each triggered by a different event:14

BlockTriggered byDefault when the block ends
route { }Every SIP request received from the networkThe request is discarded unless it was forwarded or answered
route[name]Called from other routes with route(name)Returns to the caller
branch_routeA new branch is ready but not yet sent (armed with t_on_branch())The branch is sent unless dropped
onreply_routeA reply arrives (global, or armed per transaction or branch with t_on_reply())The reply is passed to the transaction engine
failure_routeEvery branch ended with a negative reply (armed with t_on_failure())The winning negative reply is sent back to the caller
error_routeA parsing error or failed assertThe request is discarded
local_routeTM generates a request internallyThe request is sent
startup_route, timer_route, event_routeStartup, a fixed interval, or a named Event Interface eventNo SIP message is involved

Scroll the table sideways to see every column.

Two details trip up newcomers. First, a failure_route processes "the original SIP request (that was sent out)", not the reply that failed.14 Second, script variables ($var) persist only for one run of a top-level route, but may hold garbage left by the previous message the same process handled. The documentation says to initialise them before use.15

The modules that matter

The core does very little on its own: features come from modules loaded with loadmodule.30 These are the ones the rest of this guide relies on, described from their own documentation.

ModuleWhat it doesWorth knowing
registrar and usrlocRegistrar processes REGISTER "per RFC 3261", with Path support per RFC 3327.35 usrloc stores the contact bindings.36Contacts can live in memory, in SQL or in NoSQL, or in memory with asynchronous SQL sync. Since 2.4, usrloc can be distributed across a cluster.36
auth_dbDigest authentication against a subscriber table.37Supports the RFC 8760 SHA-256 and SHA-512-256 algorithms since 3.2.37
dispatcherSelects a destination from a set using hashing, round-robin, random or script-defined algorithms, and rewrites the destination URI.24Stateless, with "no guarantee of fair distribution". Probes failed destinations with OPTIONS. It can also take dynamic weights from FreeSWITCH’s Event Socket.24
load_balancerRoutes by load: it counts ongoing calls per destination and resource and picks "the destination with the largest available slot".38Needs the dialog module. Use it when your media servers have hard capacity limits.38
droutingDynamic routing by prefix, caller group, time and priority, with "Least Cost Routing (LCR)" as "a special case".39Includes stripping and prefixing, weight-based or random gateway selection, serial forking and gateway probing.39
dialogCall-level state, profiles, timeouts, termination.23The foundation for load_balancer, topology hiding with dialog support, fraud_detection and rtp_relay.38424332
rtpproxy, rtpengine, rtp_relayControl an external media relay. rtpengine rewrites SDP so that media flows through Sipwise rtpengine.31rtp_relay engages a relay once on the initial INVITE and then handles the rest of the call by itself. Since 3.6 it also has an internal "route" engine for custom SDP handling.32
b2b_entities and b2b_logicOpenSIPS’s B2BUA, built in two layers: entities implement the basic UAS and UAC functions, and logic implements the services.4041Sessions can start from the script or from an MI command (third-party call control). Scenario logic runs in dedicated routes.41
topology_hidingStrips and restores Via, Record-Route and Route headers, and replaces the Contact with the proxy’s own address.42In-dialog requests then carry no Route headers, so the script must call topology_hiding_match() to find their dialog.42
fraud_detectionWatches total calls, calls per minute, concurrent calls, sequential calls and call duration, per user and per called prefix, against warning and critical thresholds.43Requires drouting and dialog.43
clustererGroups OpenSIPS instances into clusters that exchange BIN packets for replication and shared tasks.444.0 added cluster-bridge replication, which links clusters in different data centres without a full mesh.449
stir_shakenSTIR/SHAKEN authentication and verification services (RFC 8224, RFC 8588).45In 4.0 the module builds against wolfSSL by default; OpenSSL remains a compile-time option.9

Scroll the table sideways to see every column.

Dispatcher or load_balancer? Dispatcher is the lighter tool: no dialog state, many algorithms, health checks. load_balancer knows how many calls each destination is carrying and will not exceed a configured capacity, but it needs dialog tracking for every call.2438 Our view: with two or three identical media servers and no licence limits, dispatcher is enough, and it is what the worked example below uses. When the destinations differ in size, or a channel limit is contractual, use load_balancer.

Releases: which version to run in October 2026

The release policy sets a one-year major cycle. Standard releases are "supported for 1.5 years after the initial release" and LTS releases for three years. LTS releases are "typically even-numbered", with 3.0 (standard) and 3.1 (LTS) named as exceptions.4 Once a release is unsupported, "it will not get any new bug fixes, re-packaging or new tarballs".4

The versions page lists the live state of each branch:3

BranchTypeReleasedLatest minor (per the versions page)Supported until
4.1.xDevelopment (master)Not released4.1.0-devNot applicable
4.0.xStandard23 June 20268 September 2026 (4.0.2)December 2027
3.6.xLTS23 July 20258 September 2026 (3.6.9)21 May 2028
3.5.xStandard25 July 202418 December 202518 December 2025 (ended)
3.4.xLTS19 July 20236 May 20266 May 2026 (ended)

Scroll the table sideways to see every column.

The minor version numbers come from the repository tags and the project news page, which list 4.0.2 and 3.6.9 as released on 8 September 2026.52 The page currently states: "Currently supported stable releases: 4.0 and 3.6 (LTS)."3

Two things on that page differ from the written policy. First, the policy expects a beta in the second half of March and a stable release "around the beginning of May".4 Every stable release from 3.1 to 3.6 actually came out in July, and 4.0 arrived on 23 June.3 The policy page was last modified in 2020.4 Second, 4.0 is even-numbered but not labelled LTS: its December 2027 end of support is the 18-month standard window.34 So 4.0 is the 3.0 exception repeated, and the versions page does not yet name the next LTS.

What 4.0 brings, in the project’s own words, is "radical changes within the OpenSIPS design".2 The changes include the multi-threaded TCP and TLS layer described above, a profiling framework that exports script-route timings through OpenTelemetry, "bond" sockets that let the script pick an interface even for FQDN destinations, transaction branches allocated on demand (the old limit was 32, extendable to 64), PRACK and UPDATE interworking in the dialog module, Proxy Protocol support and a new auth_web3 module.9

Our view: run 3.6 LTS in production unless you need something only 4.0 has. It is supported until May 2028, it receives the same minor-release cadence as 4.0, and its module documentation is complete. The 4.0 TCP and TLS rewrite is the strongest reason to move: TLS trunks, WebSocket clients, many persistent connections. If you do move, plan the next upgrade within 2027, because 4.0 support ends that December. Anyone still on 3.4 is unsupported: its last release was on 6 May 2026, two weeks before the project shipped the security-focused 3.6.6 and 4.0.0-rc1 releases on 20 May.32

The OpenSIPS Control Panel

OpenSIPS has no built-in GUI. The project’s answer is the OpenSIPS Control Panel (OCP), "a PHP web portal" for provisioning, operating and monitoring one or several OpenSIPS servers from a single instance.46 Its tools cover SIP accounts with aliases and permissions, load balancing and dispatcher sets, dialplans and dynamic routing, CDRs, statistics and SIP traces, plus integrations with Monit and Homer.46 It is GPL-2.0 software, and its repository describes "more than 18 tools".48

OCP versions are tied to OpenSIPS branches: OCP 9.4.0 matches OpenSIPS 4.0 and OCP 9.3.6 matches 3.6, and both are maintained.46 9.4.0 talks to 4.0 using 4.0’s renamed MI commands. Its MI Commands tool asks the server which commands it supports and which parameter sets each accepts, and it refuses to send a call that cannot succeed. The project notes that this pre-check "is a usability safeguard, not an access control".47

Our view: OCP is useful for exactly the tables this guide is about: dispatcher sets, drouting rules, subscribers. Treat it as an administrative console with full control of your SIP routing, and keep it off the public internet, behind a VPN or an access proxy. Many teams instead edit the tables directly and keep them in version control. Both approaches work, as long as you choose one deliberately.

Typical deployments

  • Load balancer in front of Asterisk or FreeSWITCH. This is the most common first deployment and the one the feature list names directly ("SIP front end for gateways/asterisk").1 OpenSIPS takes all traffic on one address, spreads new calls with dispatcher or load_balancer, takes failed boxes out of rotation and fails calls over. The shipped loadbalancer.m4 template is a starting point for this design.29
  • Carrier and wholesale routing. drouting selects gateways by prefix, caller group, time and priority, and handles LCR and serial failover across carriers.39 Add fraud_detection for per-customer thresholds and stir_shaken for call authentication.4345
  • Session border controller. topology_hiding conceals the internal network, rtpengine or rtp.io anchors media, the B2BUA modules terminate and re-originate calls where pure proxying is not enough, and 4.0 adds PRACK and UPDATE interworking for IMS-facing carriers.423110419
  • Registrar and edge proxy at scale. registrar, usrloc and auth_db serve large numbers of phones. With the clusterer module, usrloc can share or federate contacts across nodes.353644

OpenSIPS itself contains nothing specific to the United States or Canada. Number formats, 911 and 9-1-1 obligations and STIR/SHAKEN policy are applied by your script and your carrier, not by the server.

Worked example: two media servers behind dispatcher and rtpengine

The scenario: a carrier delivers inbound calls for your numbers to a single public address. Behind it are two media servers on a private network, Asterisk or FreeSWITCH, running the phone menu. OpenSIPS spreads new calls across them, retries the other server if one fails, removes a dead server from rotation until it answers health checks again, and relays audio through rtpengine. The media servers therefore never need a public address.

RoleAddress (example)
OpenSIPS and rtpengine, public side203.0.113.10
OpenSIPS and rtpengine, private side10.0.0.10
Media server A, media server B10.0.0.21, 10.0.0.22
Carrier signalling addresses198.51.100.5, 198.51.100.6

Scroll the table sideways to see every column.

The configuration targets OpenSIPS 3.6 LTS. We compared every function and module parameter it uses against the 4.0 module documentation and found the same signatures there. The only 4.0 difference that affects this guide is the MI command names, covered at the end.2425 We built it from the module documentation and the shipped templates. We have not run it against a live carrier, so check it with opensips -C and test with your own traffic before relying on it.

1. rtpengine: two named interfaces

# /etc/rtpengine/rtpengine.conf on the proxy host
[rtpengine]
interface = priv/10.0.0.10;pub/203.0.113.10
listen-ng = 127.0.0.1:2223

rtpengine’s interface option takes [NAME/]IP. In the config file, several interfaces go on one line, separated by semicolons.33 We call them priv and pub rather than internal and external on purpose. If interfaces with those two names are not configured, rtpengine treats the names as legacy IPv4/IPv6 selectors, so a typo would fail without an error.34 listen-ng is the control socket. The manual recommends binding it to 127.0.0.1.33 Open UDP 30000 to 40000 on the public side, rtpengine’s default media port range.33

2. The destination sets

# /etc/opensips/dbtext/version
table_name(string) table_version(int)
dispatcher:9

# /etc/opensips/dbtext/dispatcher
id(int,auto) setid(int) destination(string) socket(string,null) state(int) probe_mode(int) weight(string) priority(int) attrs(string,null) description(string,null)
1:1:sip\:10.0.0.21\:5060::0:1:1:0::media-a
2:1:sip\:10.0.0.22\:5060::0:1:1:0::media-b
3:2:sip\:198.51.100.5\:5060::0:0:1:0::carrier-1
4:2:sip\:198.51.100.6\:5060::0:0:1:0::carrier-2

Since the old text-file provisioning was dropped, dispatcher reads its destinations only from a database. For a small static list, the documentation suggests the db_text driver, which emulates a database with text files.24 Each table is a file. The first line declares the columns, fields are separated by :, and a literal colon inside a value must be escaped, hence sip\:10.0.0.21\:5060.28 The column list and table version 9 are copied from the db_text schema in the 3.6 source tree, and dispatcher requires the matching version row.2724

  • Set 1 holds the two media servers and set 2 the carrier’s addresses. Set 2 is never used for routing. It serves as an allow-list for ds_is_in_list(), so one module covers both jobs.
  • state 0 means active (1 is inactive and 2 is probing). probe_mode 1 makes dispatcher ping that destination continuously, not only after a failure. We set it on the media servers and leave it at 0 for the carrier, whose addresses we have no reason to ping.26
  • Weights of 1 make round-robin alternate one call each. Under algorithm 4, a destination is chosen as many consecutive times as its weight.24

3. Global parameters and sockets

####### Global parameters #######
log_level=3
xlog_level=3
stderror_enabled=no
syslog_enabled=yes
syslog_facility=LOG_LOCAL0

udp_workers=4

socket=udp:203.0.113.10:5060     # public: the carrier sends calls here
socket=udp:10.0.0.10:5060        # private: faces the two media servers

The logging lines are the shipped defaults: log level 3, written to syslog facility LOG_LOCAL0.30 udp_workers=4 gives four worker processes per UDP interface, so eight here, which is ample for a two-server cluster.12 Each socket line follows protocol:address[:port]. socket replaced listen in 3.1 with the same format, so older tutorials that use listen= still describe the same thing.12

4. Modules

####### Modules #######
mpath="/usr/local/lib/opensips/modules/"   # use your package's path

loadmodule "proto_udp.so"
loadmodule "signaling.so"
loadmodule "sl.so"
loadmodule "tm.so"
modparam("tm", "fr_timeout", 5)
modparam("tm", "fr_inv_timeout", 30)

loadmodule "rr.so"
loadmodule "maxfwd.so"
loadmodule "sipmsgops.so"

loadmodule "mi_fifo.so"
modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")

loadmodule "db_text.so"
loadmodule "dispatcher.so"
modparam("dispatcher", "db_url", "text:///etc/opensips/dbtext")
modparam("dispatcher", "ds_ping_interval", 10)
modparam("dispatcher", "ds_ping_from", "sip:[email protected]")
modparam("dispatcher", "ds_probing_threshold", 2)

loadmodule "rtpengine.so"
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
  • proto_udp, signaling, sl, tm, rr, maxfwd and sipmsgops are the same base set the shipped templates load.3029
  • fr_timeout 5 and fr_inv_timeout 30 are the template values, not the module defaults (30 and 120 seconds).1729 fr_timeout fires when no final reply has arrived. fr_inv_timeout starts once a provisional reply has arrived, so "fast failover (no 100 trying from gateway) can be achieved by setting fr_timeout to low values".17 In practice, a media server that is down produces a 408 within five seconds, and the call moves to the other one.
  • mi_fifo is the management interface that opensips-cli talks to. The FIFO path is the one in the shipped default config.30
  • ds_ping_interval 10 sends an OPTIONS ping every ten seconds. The default, 0, disables pinging.24 ds_ping_from sets the From header of those pings.24 ds_probing_threshold is the number of failures needed before a destination moves to probing (default 3). We use 2, so two consecutive failed pings, or failed calls marked in the failure route, take a server out of rotation.2426 The server comes back on its own: a 200 reply to a ping clears the probing state and resets the failure counter.26
  • rtpengine_sock points at rtpengine’s listen-ng socket.31

5. The main request route

####### Routing logic #######
route {
    if (!mf_process_maxfwd_header(10)) {
        send_reply(483, "Too Many Hops");
        exit;
    }

    # Who sent this? Set 2 = carrier, set 1 = our media servers.
    $var(from_carrier) = 0;
    if (ds_is_in_list($si, 0, 2)) {
        $var(from_carrier) = 1;
    } else if (!ds_is_in_list($si, 0, 1)) {
        xlog("L_WARN", "dropping $rm from unknown source $si\n");
        exit;
    }

    if (has_totag()) {
        route(in_dialog);
        exit;
    }

    if (is_method("CANCEL")) {
        if (t_check_trans())
            t_relay();
        exit;
    }

    t_check_trans();

    if (is_method("OPTIONS")) {
        send_reply(200, "OK");
        exit;
    }

    if (!is_method("INVITE") || $var(from_carrier) != 1) {
        send_reply(403, "Forbidden");
        exit;
    }

    if ($rU == NULL) {
        send_reply(484, "Address Incomplete");
        exit;
    }

    record_route();

    if (!ds_select_dst(1, 4, "f")) {
        send_reply(503, "No media server available");
        exit;
    }
    $socket_out = "udp:10.0.0.10:5060";

    if (has_body_part("application/sdp"))
        rtpengine_offer("in-iface=pub out-iface=priv replace-origin replace-session-connection");

    t_on_reply("media_answer");
    t_on_failure("next_media_server");
    if (!t_relay())
        send_reply(500, "Internal Error");
    exit;
}
  • mf_process_maxfwd_header(10) adds a Max-Forwards header of 10 if there is none, otherwise decrements it, and returns false when it is already zero, which we answer with 483; which stops routing loops.22 It is the first check in the shipped scripts.3029
  • Source classification. ds_is_in_list($si, 0, 2) is true when the source IP ($si) matches a set-2 address on any port.2415 Requests from any address in neither set are dropped silently with exit. A request that is neither forwarded nor answered is discarded at the end of the route, and sending no reply gives SIP scanners nothing to work with.14 $var(from_carrier) is reset first because script variables can carry over from the previous message the same process handled.15
  • has_totag() identifies in-dialog requests (ACK, BYE, re-INVITE), which go to route[in_dialog].21
  • CANCEL is relayed only if it matches a transaction we know, as in the shipped scripts.30 t_check_trans() then absorbs retransmissions of requests already being handled.1730
  • OPTIONS from either side is answered with a 200, which keeps the carrier’s and the media servers’ health checks happy. Every other initial request must be an INVITE from the carrier. This proxy accepts inbound calls only, so a compromised media server cannot use it to place calls out.
  • record_route() keeps the proxy in the path of every later request in the call.20 The call enters on one interface and leaves on the other, so the rr module inserts two Record-Route headers. enable_double_rr defaults to 1 for "two disconnected networks".20
  • ds_select_dst(1, 4, "f") chooses from set 1 with algorithm 4 (weighted round-robin) and sets the destination URI. The f flag stores the remaining destinations so that ds_next_dst() can try them later. Without it there is nothing to fail over to.24 Destinations marked inactive or probing are skipped.24
  • $socket_out is writable, and until it is set the outbound socket is the inbound one.15 We set the private socket explicitly so the INVITE leaves on 10.0.0.10.
  • rtpengine_offer() rewrites the SDP so that the media server sends its audio to rtpengine’s private address.31 in-iface and out-iface name the interface the message came in on and the one it leaves on.31 Direction only needs to be given in the initial offer, because rtpengine remembers it for the rest of the call.34 replace-origin and replace-session-connection also rewrite the SDP o= line and the session-level c= line.31 We check has_body_part("application/sdp") first.21
  • t_on_reply() and t_on_failure() arm the reply and failure routes. t_relay() creates the transaction and forwards the request.17 If forwarding fails immediately, we answer 500. send_reply() is allowed in a request route.19

6. In-dialog requests

route[in_dialog] {
    if (is_method("ACK") && t_check_trans()) {
        t_relay();
        exit;
    }

    if (!loose_route()) {
        send_reply(404, "Not here");
        exit;
    }

    if ($var(from_carrier) == 1)
        $socket_out = "udp:10.0.0.10:5060";
    else
        $socket_out = "udp:203.0.113.10:5060";

    if (is_method("BYE")) {
        rtpengine_delete();
    } else if (is_method("INVITE") && has_body_part("application/sdp")) {
        rtpengine_offer("replace-origin replace-session-connection");
        t_on_reply("media_answer");
    }

    if (!t_relay())
        send_reply(500, "Internal Error");
}
  • The ACK-then-loose_route() order is the shipped idiom. An ACK that matches a transaction is a hop-by-hop ACK for a negative reply. Anything else in-dialog must carry our Route headers, or it receives a 404.3020
  • The outbound socket is set from the direction of the request rather than inferred: requests from the carrier go out on the private side, and requests from a media server go out on the public side.15
  • A BYE tears down the rtpengine session.31 A re-INVITE with SDP (hold, or a codec change) is a new offer, and its reply goes through the same answer route.

7. Replies and failover

onreply_route[media_answer] {
    if (has_body_part("application/sdp"))
        rtpengine_answer("replace-origin replace-session-connection");
}

failure_route[next_media_server] {
    if (t_was_cancelled()) {
        rtpengine_delete();
        exit;
    }

    if (t_check_status("(408)|(5[0-9][0-9])")) {
        if (t_check_status("408"))
            ds_mark_dst("p");

        if (ds_next_dst()) {
            $socket_out = "udp:10.0.0.10:5060";
            t_on_reply("media_answer");
            t_on_failure("next_media_server");
            t_relay();
            exit;
        }
    }

    rtpengine_delete();
}
  • rtpengine_answer() runs on any reply with SDP (183 or 200) and rewrites the media server’s answer so that the carrier sends audio to rtpengine’s public address.31
  • In the failure route, t_was_cancelled() means the caller hung up before the call was answered, so we clean up the media session and stop.17
  • t_check_status() matches the winning negative reply.17 On a timeout (408) or any 5xx we try the next media server. Only a timeout counts against the server through ds_mark_dst("p"), because a 503 can be a deliberate refusal from a live box.24
  • ds_next_dst() loads the next destination saved by the f flag.24 The failure route works on the original request as it was sent out, so the second attempt carries the same rewritten SDP and the same rtpengine session completes when B answers.14 We re-arm the failure route, as the shipped template does, so that a second failure is also handled.29
  • For any other negative reply (486 Busy, 404, or both servers failed), the media session is deleted and the route ends. By default the winning negative reply then goes back to the carrier.14 send_reply() is not permitted in a failure route. If you want a custom final response there, use t_reply().1917

8. Checking and operating it

# check the script without starting the server
opensips -C -f /etc/opensips/opensips.cfg

# once running: list the destinations and their state (3.6 name)
opensips-cli -x mi ds_list

# take media-b out of rotation for maintenance, then bring it back
opensips-cli -x mi ds_set_state i 1 sip:10.0.0.22:5060
opensips-cli -x mi ds_set_state a 1 sip:10.0.0.22:5060

# after editing the dispatcher table
opensips-cli -x mi ds_reload

-c checks the configuration file. -C also checks that every function is used only in routes that allow it, which catches errors such as calling send_reply() in a failure route.16 In 3.6 the dispatcher MI commands are ds_list, ds_set_state and ds_reload. In 4.0 they are dispatcher:list, dispatcher:set_state and dispatcher:reload, and the old names are documented as obsolete.2425 ds_reload keeps the current state of destinations unless you pass inherit_state=n.24

On the media servers, accept calls only from 10.0.0.10 and treat that address as your trunk. They will see every call arrive from the proxy, with audio from rtpengine’s private address.

What this example deliberately leaves out

TLS and SRTP to the carrier, late-offer calls (an INVITE without SDP, whose offer arrives in the 200 and the answer in the ACK, which the rtpengine functions also support),31 digest authentication of the carrier, rate limiting, CDRs with acc, and high availability. A single proxy host is a single point of failure. For HA, pair two hosts on a floating address and replicate dispatcher state with the clusterer module.2444 If you prefer not to manage offer and answer yourself, rtp_relay_engage("rtpengine") on the initial INVITE hands the whole media session to the rtp_relay module. It requires the dialog module.32

A note on the shipped template

We read the shipped loadbalancer.m4 in the 3.6 branch while writing this, and found two issues worth knowing about if you start from it. In dispatcher mode it calls ds_select_dst(1,4) without the f flag, but its failure route relies on ds_next_dst(). It also defines DISABLE_PINGING while the conditionals test DISABLE_PROBING, so the "no pinging" switch has no effect.29 According to the dispatcher documentation, the first issue means failover has no alternative destination to move to.24 The same lines are present on the master branch as of today.5

OpenSIPS and Kamailio: same roots, different direction

Kamailio calls itself the "successor of former OpenSER and SER" and is licensed GPLv2+. Its latest release is 6.1.5, dated 15 October 2026.49 OpenSIPS describes itself as OpenSER’s continuation.8 Both claims are historically defensible.

The differences are mostly about where each project has put its effort. These are the ones we can document from first-party sources:

  • Scripting. Kamailio’s KEMI framework, introduced in 5.0.0, lets you write the routing logic in JavaScript, Lua, Python, Ruby or Squirrel instead of the native configuration language.50 OpenSIPS routing is written in its native script language, as everything in this guide is.
  • Built-in application features. OpenSIPS has a two-layer B2BUA in the main tree, an in-process RTP relay since 3.6, and an official web control panel maintained alongside each release.401046
  • Release discipline. OpenSIPS publishes a written cadence with three-year LTS branches and states on one page which branches are supported and until when.43

Our view: pick by team and ecosystem, not by benchmark. If your engineers would rather write routing in Python or Lua, Kamailio’s KEMI is a real advantage. If you want B2BUA features, a panel and a predictable LTS date in one project, OpenSIPS is the more complete package. We think the proxy is rarely the bottleneck with either project; the media servers behind it are.

When OpenSIPS is the wrong tool

  • One PBX, one site. A single Asterisk or FreeSWITCH box can register its own phones and talk to its own trunk. Adding a proxy in front doubles the number of things to secure and debug and brings no benefit until you have more than one media server, or a carrier-facing edge to protect.
  • You need IVR, voicemail or recording. OpenSIPS routes signalling. It does not answer calls with prompts. That is a PBX or media server job, or a hosted service.
  • Nobody on the team will own the script. An OpenSIPS configuration is code and needs the same care: version control, review, a test environment, and someone who reads the release notes. The risk is not that OpenSIPS fails. It is a routing mistake that nobody understands well enough to see.

For a business that just wants its number answered by a phone menu that sends callers to the right person, any of these stacks is more machinery than the problem needs. That is the gap a hosted auto attendant fills. Ours is one, and how it works describes what it does.

Sources

Every regulatory and pricing claim on this page links to the primary source it came from. Figures were checked on the date shown at the top of the page; rules and prices change, so verify before relying on one.

  1. OpenSIPS project, "About OpenSIPS" (the list of roles: registrar, proxy, redirect server, presence agent, back-to-back user agent, load balancer, front end for Asterisk, NAT traversal unit, application server). Retrieved 26 Oct 2026
    https://opensips.org/About/About
  2. OpenSIPS project home page (self-description; news items for 4.0.0 stable on 23 June 2026, security-focused 4.0.0-rc1 and 3.6.6 on 20 May 2026, 4.0.2 and 3.6.9 on 8 September 2026). Retrieved 26 Oct 2026
    https://www.opensips.org/
  3. OpenSIPS project, "Available versions" (release dates, LTS labels, last release and end of support for every branch; 4.1.0-dev on master). Retrieved 26 Oct 2026
    https://opensips.org/About/AvailableVersions
  4. OpenSIPS project, release policy (one-year major cycle, standard releases supported 1.5 years, LTS 3 years, even/odd numbering and its exceptions). Page last modified 6 November 2020. Retrieved 26 Oct 2026
    https://opensips.org/Development/Development
  5. OpenSIPS source repository on GitHub (repository description, C source, branches 1.4 to 4.0 plus master, tags up to 4.0.2 and 3.6.9). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips
  6. OpenSIPS repository, COPYING (GNU GPL version 2; "OpenSIPS software is derived from SER (SIP Express Router) project developed by FhG FOKUS"). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips/blob/master/COPYING
  7. Kamailio project, "History" (SER started 2 September 2001 at FhG Fokus, released under GPL in autumn 2002, OpenSER started 14 June 2005, renamed Kamailio 28 July 2008, the OpenSIPS fork, Kamailio 3.0.0 on 11 January 2010, SER integration completed 20 December 2012). Retrieved 26 Oct 2026
    https://www.kamailio.org/w/history/
  8. Kamailio users mailing list archive, "OpenSIPS project", 4 August 2008: the OpenSIPS founder at Voice System announcing the new project and its first release, 1.4.0. 4 August 2008; retrieved 26 Oct 2026 through our scraping router (the opensips.org list archive returned HTTP 502)
    https://lists.kamailio.org/pipermail/users/2008-August/018804.html
  9. OpenSIPS project, "Version overview 4.0.0" (multi-threaded TCP, TLS, profiling, bond sockets, dynamic TM branches, PRACK/UPDATE, Proxy Protocol, auth_web3, cluster-bridge replication, wolfSSL default for stir_shaken). Retrieved 26 Oct 2026
    https://opensips.org/About/Version-Overview-4-0-0
  10. OpenSIPS project, "Version overview 3.6.0" (LTS; dynamic sockets, rtp.io in-process media relaying, $config variables, Redis, AWS, Janus, unified branching, structured SDP). Retrieved 26 Oct 2026
    https://opensips.org/About/Version-Overview-3-6-0
  11. OpenSIPS blog, "TCP/TLS rework in OpenSIPS 4.0", 8 April 2026. 8 April 2026
    https://blog.opensips.org/2026/04/08/tcp-tls-rework-in-opensips-4-0/
  12. OpenSIPS 3.6 manual, Core parameters (socket, udp_workers, tcp_workers, timer_workers, auto_scaling_profile, log_level, mpath; listen replaced by socket in 3.1; 32 MB default shared memory). The 4.0 page adds tcp_threads. Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/script-coreparameters/
  13. OpenSIPS 4.0 manual, Core parameters (tcp_workers and tcp_threads under the 4.0 TCP design). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/script-coreparameters/
  14. OpenSIPS 3.6 manual, Script routes (route, branch_route, failure_route, onreply_route, error_route, local_route, startup_route, timer_route, event_route; triggers and default actions). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/script-routes/
  15. OpenSIPS 3.6 manual, Core variables ($var scope and initialisation, $si, $socket_in, read-write $socket_out). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/script-corevar/
  16. OpenSIPS repository, opensips.8 manual page, 3.6 branch (-f, -c, -C, -m). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips/blob/3.6/opensips.8
  17. OpenSIPS 3.6 module documentation, TM (stateful processing, shared-memory clones, t_relay, t_reply, t_check_status, t_was_cancelled, t_on_failure, fr_timeout and fr_inv_timeout defaults). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/tm/
  18. OpenSIPS 3.6 module documentation, SL (stateless replies). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/sl/
  19. OpenSIPS 3.6 module documentation, SIGNALING (send_reply and the routes it may be used from). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/signaling/
  20. OpenSIPS 3.6 module documentation, RR (record_route, loose_route, enable_double_rr default 1). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/rr/
  21. OpenSIPS 3.6 module documentation, SIPMSGOPS (is_method, has_totag, has_body_part). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/sipmsgops/
  22. OpenSIPS 3.6 module documentation, MAXFWD (mf_process_maxfwd_header adds the header if absent, decrements it, returns false at 0). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/maxfwd/
  23. OpenSIPS 4.0 module documentation, DIALOG (create_dialog, termination on BYE or default_timeout, profiling). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/dialog/
  24. OpenSIPS 3.6 module documentation, DISPATCHER (algorithms 0 to 10, flags, ds_next_dst, ds_mark_dst, ds_is_in_list, probing parameters, FreeSWITCH ESL weights, ds_list and ds_reload MI commands, db_text provisioning). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/dispatcher/
  25. OpenSIPS 4.0 module documentation, DISPATCHER (same functions and parameters; MI commands renamed dispatcher:list, dispatcher:reload, dispatcher:set_state). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/dispatcher/
  26. OpenSIPS source, modules/dispatcher/dispatch.c, 3.6 branch (state and probe_mode column values; OPTIONS reply handling that returns a destination to active; the probing threshold). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips/blob/3.6/modules/dispatcher/dispatch.c
  27. OpenSIPS source, scripts/dbtext/opensips/dispatcher and scripts/dbtext/opensips/version, 3.6 branch (dispatcher table columns; table version 9). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips/tree/3.6/scripts/dbtext/opensips
  28. OpenSIPS 3.6 module documentation, DB_TEXT (text:// URLs, table file format, escaping of ":"). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/db_text/
  29. OpenSIPS source, examples/templates/loadbalancer.m4, 3.6 branch (the shipped load-balancer template). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips/blob/3.6/examples/templates/loadbalancer.m4
  30. OpenSIPS source, etc/opensips.cfg, 3.6 branch (the shipped residential script: section layout, in-dialog handling idiom). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips/blob/3.6/etc/opensips.cfg
  31. OpenSIPS 3.6 module documentation, RTPENGINE (rtpengine_sock, sets and failover, rtpengine_offer/answer/delete/manage, in-iface and out-iface, replace-origin, replace-session-connection). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/3-6/modules/rtpengine/
  32. OpenSIPS 4.0 module documentation, RTP_RELAY (rtp_relay_engage, dialog dependency, the internal "route" engine added in 3.6). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/rtp_relay/
  33. Sipwise rtpengine manual (--interface NAME/IP syntax and the config-file form, --listen-ng, port-min and port-max defaults of 30000 and 40000). Retrieved 26 Oct 2026 (also read at rtpengine.readthedocs.io)
    https://github.com/sipwise/rtpengine/blob/master/docs/rtpengine.md
  34. Sipwise rtpengine, "ng control protocol" (direction is only needed in the initial offer; legacy meaning of the names internal and external). Retrieved 26 Oct 2026
    https://rtpengine.readthedocs.io/en/latest/ng_control_protocol.html
  35. OpenSIPS 4.0 module documentation, REGISTRAR (REGISTER processing per RFC 3261, Path support per RFC 3327). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/registrar/
  36. OpenSIPS 4.0 module documentation, USRLOC (in-memory, SQL or NoSQL storage; working_mode_preset; distributed user location since 2.4). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/usrloc/
  37. OpenSIPS 4.0 module documentation, AUTH_DB (database-backed digest authentication; RFC 8760 SHA-256 and SHA-512-256 since 3.2). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/auth_db/
  38. OpenSIPS 4.0 module documentation, LOAD_BALANCER (load-based routing by largest available slot, probing modes, dialog dependency). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/load_balancer/
  39. OpenSIPS 4.0 module documentation, DROUTING (prefix, caller-group, time and priority rules; least cost routing as a special case; gateway probing). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/drouting/
  40. OpenSIPS 4.0 module documentation, B2B_ENTITIES (the lower B2BUA layer: server and client entities). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/b2b_entities/
  41. OpenSIPS 4.0 module documentation, B2B_LOGIC (the upper B2BUA layer, script-driven scenarios, third-party call control over MI). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/b2b_logic/
  42. OpenSIPS 4.0 module documentation, TOPOLOGY_HIDING (stripping Via, Record-Route and Route; topology_hiding_match for in-dialog requests). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/topology_hiding/
  43. OpenSIPS 4.0 module documentation, FRAUD_DETECTION (five monitored parameters per user and prefix, warning and critical thresholds; depends on drouting and dialog). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/fraud_detection/
  44. OpenSIPS 4.0 module documentation, CLUSTERER (BIN-level node mesh, capabilities and data sync, cluster-bridge replication added in 4.0). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/clusterer/
  45. OpenSIPS 4.0 module documentation, STIR_SHAKEN (authentication and verification services, RFC 8224 and RFC 8588). Retrieved 26 Oct 2026
    https://docs.opensips.org/manual/4-0/modules/stir_shaken/
  46. OpenSIPS Control Panel site (PHP portal for provisioning and monitoring several OpenSIPS servers; version compatibility table; tool list). Retrieved 26 Oct 2026
    https://controlpanel.opensips.org/
  47. OpenSIPS blog, "The MI Commands tool in Control Panel 9.4.0", 12 August 2026. 12 August 2026
    https://blog.opensips.org/2026/08/12/the-mi-commands-tool-in-control-panel-9-4-0/
  48. OpenSIPS Control Panel source repository on GitHub (GPL-2.0 licence; "more than 18 tools"). Retrieved 26 Oct 2026
    https://github.com/OpenSIPS/opensips-cp
  49. Kamailio project home page ("successor of former OpenSER and SER", GPLv2+, latest release 6.1.5 dated 15 October 2026). Retrieved 26 Oct 2026
    https://www.kamailio.org/w/
  50. Kamailio, "KEMI framework" tutorial (routing logic in JavaScript, Lua, Python, Ruby or Squirrel; introduced in Kamailio 5.0.0). Retrieved 26 Oct 2026
    https://kamailio.org/docs/tutorials/devel/kamailio-kemi-framework/