Multi-tenant hotel and restaurant PBX on Asterisk with PJSIP realtime (Filantis): design notes and three fixes

Disclosure: I’m the developer of Filantis, a commercial product, which is why this is in the Commercial category.

Filantis is a multi-tenant business phone system (cloud PBX or on-premise) built on Asterisk, with hotel, restaurant and office features built into the PBX: wake-up calls, room status from the room phone, an outside line that opens at check-in, a restaurant order line with kitchen tickets, and ring groups per department.

How it is built on Asterisk

  • PJSIP with realtime configuration, one set of tables per tenant.
  • Dialplan generated per tenant, with hand-written contexts for the shared parts.
  • An ARI application for the call-attendant (IVR) menus.
  • A WebRTC softphone in the browser over WSS, plus Android and Windows SIP softphones.
  • ConfBridge for group video calls.

Three problems that may save someone else a day

  1. Same extension number in every tenant. Every tenant’s first extension is 100, and on one shared Asterisk the realtime rows collided, so a new tenant could take over another tenant’s extension. Endpoint, AOR and auth objects are now named 100@tenant-domain, while the auth username stays a bare 100. Phones still register as user 100 against their own domain, and Asterisk matches the endpoint from user@domain in the From header without extra configuration.

  2. Calls answer, silence both ways, drop at exactly 32 seconds. external_media_address and external_signaling_address were set to the server’s host name, and /etc/hosts mapped that name to 127.0.1.1. Asterisk therefore sent c=IN IP4 127.0.1.1 in the SDP. pjsip show channelstats showing zero RTP in both directions on both legs was the clue. Writing the public IP as a literal fixed it.

  3. Browser softphone would not register over WSS on Asterisk 20. With chan_sip loaded, it claimed the “sip” WebSocket subprotocol and res_pjsip_transport_websocket declined to load. noload => chan_sip.so under [modules] and a clean restart fixed it.

A question for people running shared multi-tenant Asterisk: do you name endpoints ext@domain like this, or do you put Kamailio in front and keep Asterisk single-tenant behind it? I’d like to hear where the ext@domain approach stops scaling.

There is a free hosted plan (10 extensions) if you want to look at it: https://filantis.com

You may use tenantidext@tenant-domain for an extension isolation in a multi-tenant asterisk realtime deployment.

Wouldn’t it be simpler to run each tenant on their own Asterisk instance?

You can do the separation with containers on Linux.

for a small operation spinning up small nodes are ok
but for mass managing you need 1 centralized place or else it become a big burden.

we use ext as id = $number-$tenant (100-alice 100-bob) it works very nice

Thanks all, useful replies.

basit.engg, that is close to what we do. The tenant’s domain acts as the tenant id, so 100@acme.example.com is unique across the box.

@ldo we considered it. Separate instances give cleaner isolation: a bad dialplan or a reload in one tenant cannot touch another. But every instance then needs its own SIP port or a proxy in front that routes by domain, and at that point you are running Kamailio anyway. Most of our tenants have fewer than 20 extensions, so one Asterisk with realtime is far fewer moving parts: one RTP range, one set of trunks, one thing to upgrade. The cost is that isolation lives in dialplan contexts and object naming, so it has to be tested on every change.

@benphone good to hear the 100-alice form holds up at scale. One reason I went with @domain is that the default username identifier already tries user@domain from the From header before the bare user, so I needed no identify sections. With 100-alice, do the phones register with the username 100-alice, or do you map a bare 100 to it somewhere?

You could use Kamailio or Asterisk as a gateway between all the containers and the rest of the Internet (including upstream VoIP providers). The gateway instance would be under your complete control, of course, and could implement whatever routing policy you specify. Tenant instances would have no way out (or in) without going through that.

Keeping track of which vm belongs to which tenant will become a nightmare as they scale

Not VMs, containers. Much lighter weight. They all share the same kernel, just different userlands isolated via the Linux namespace system.

You can package up a container template and instantiate it multiple times, once for each tenant. The separation would be much more robust and further-reaching than relying on Asterisk itself to keep users separate. For example, you can allocate resource usage on a per-container (i.e. per-tenant) basis.

yes .

A short update for anyone following this thread from the hotel side: the hotel features now have their own page, Hotel phone system: hotel PBX with wake-up calls and room-to-room calling — Filantis

What a hotel gets on the phone system:

  • Guests set their own wake-up calls from the room phone. If a call goes unanswered it retries, then alerts the front desk.
  • Housekeeping reports a room as clean or ready from the handset in that room, so the desk sees status change without a radio call.
  • The outside line opens at check-in and closes at check-out.
  • The desk decides which rooms may call each other.
  • A front desk with guest bills and invoices is included, so a small property does not need a separate PMS.

It runs in the cloud or on a server in the hotel. Questions from anyone running Asterisk in hotels are welcome here.