Multitenant deployment of Jitsi

@Aaron_K_van_Meerten what is the recommended approach for multi tenant deployment of Jitsi? E.g. we are going to share one Jitsi instance for multiple tenants, and also we would like to restrict users from tenantA to join meetings of tenantB.

Just to follow up on this topic from the Jitsi Call today — it would be really helpful to have more clarity on the multi-tenancy strategy that is currently being used by Jitsi in Production. More specifically, understanding the specifics on additional prosody modules or logic, as well as nginx config and config.js details to support separation between tenants, supporting a /tenant/meetingId URL structure, tenant/subdomain sequestering in JWT tokens/claims, and whatever “dynamic tenant MUC” setup that was mentioned on the call today.

Thanks a lot for all your help with this!


@Aaron_K_van_Meerten could you please take a look?

Hi, I’ll be working on this over the next week or so. The process will be to move the prosody modules into the open source, and develop documentation around this setup. This isn’t my main work priority this week so I may not have anything to show until probably late next week. If you haven’t heard anything by the next community call feel free to ask about it again and we’ll have an update.



@Aaron_K_van_Meerten here is what I have found related to multi tenancy.

  1. Official Jitsi Meet page generates muc hostname based on url.
    E.g. if you visit URL, then config.hosts.muc will be set to Subdomain here is orange. So, is it correct, that putting subdomain names to muc we can achieve multi tenant setup? What else config should be set for Prosody?
  2. Now I am aware about only two config options:
    enable_domain_verifiation should be set to true
    muc_mapper_domain_base should be set to

The question is: is it enough or something else should be done?

Thanks a lot for all your help with this!!!

@Aaron_K_van_Meerten I’m curious about the multi-tenancy approach. Is this understanding correct?

Jitsi-Meet (JS) would use
A prosody plugin similar to mod_filter_iq_rayo rewrites that jid to
and “” is configured in the prosody cfg as a muc.

Is that correct?
Is just that prosody plugin missing from jitsi-meet/tree/master/resources/prosody-plugins ?


That’s basically correct. We call the


syntax the “internal” representation as this module rewrites all IQs which reference this value in incoming and outgoing hooks. This way the client only has to know about the “external” representation below, and we don’t have to pre-provision tenants in the prosody configuration

However certain pieces such as prosody modules end up parsing the internal representation as well.

@ivanr you are correct. The final step would be to enable the mod_muc_domain_mapper module which we haven’t yet release but I hope to get time for mid-next week.

@Aaron_K_van_Meerten good news, thanks a lot!

Hi Aaron,

have you managed to installa multi Domain Installation.
I would be intrested.
How can i do this ?

reg Karl

The muc_domain_mapper.lua plugin is now open source, and included in the jitsi-meet package. There are example config files for this mode found here:

Let me know if this is what you’re looking for?

1 Like

@Aaron_K_van_Meerten Do we know how this to be enabled in docker set up? I have two different URLs expected to share same JVB in the back end.

There’s two concerns here. The first is the URL -> jitsi-meet client configuration. This is done via config.js, where the conference server is configured based on incoming URL. On and in the debian repo, we use nginx regexp extraction to variables and server-side includes to map the first part of path in the URL if there is one, to a custom conference server, so URLs like PREFIX/ROOM end up with a config.js settings hosts muc as, for instance. The existing docker-jitsi-meet doesn’t currently support this but could be set up to do so by adding a few more configuration stanzas to nginx, and config.js. If you wish to be mapping different incoming DNS to different prefixes, a different nginx config would be used.

The second concern is prosody. Luckily the prosody modules are already made available in the prosody side, but they need to be configured via muc_mapper_domain_base variable, and the muc_domain_mapper as an enabled module on the muc component. This can be done with environment variables.

See this folder for example configurations for nginx, config.js and prosody: jitsi-meet/doc/example-config-files/multidomain at master · jitsi/jitsi-meet · GitHub

1 Like

Thank you for the response. I have configure it but getting error below.

Jicofo 2021-01-14 07:36:34.448 WARNING: [80] org.jitsi.jicofo.xmpp.FocusComponent.processIQ() (serving component ‘Jitsi Meet Focus’) Unexpected exception while processing IQ stanza: Failed to join the room
at org.jitsi.impl.protocol.xmpp.ChatRoomImpl.joinAs(
at org.jitsi.impl.protocol.xmpp.ChatRoomImpl.join(
at org.jitsi.jicofo.JitsiMeetConferenceImpl.joinTheRoom(
at org.jitsi.jicofo.JitsiMeetConferenceImpl.start(
at org.jitsi.jicofo.FocusManager.conferenceRequest(
at org.jitsi.jicofo.FocusManager.conferenceRequest(
at org.jitsi.jicofo.FocusManager.conferenceRequest(
at org.jitsi.jicofo.xmpp.FocusComponent.handleConferenceIq(
at org.jitsi.jicofo.xmpp.FocusComponent.handleIQSetImpl(
at org.jitsi.xmpp.component.ComponentBase.handleIQSet(
at org.xmpp.component.AbstractComponent.processIQRequest(
at org.xmpp.component.AbstractComponent.processIQ(
at org.xmpp.component.AbstractComponent.processQueuedPacket(
at org.xmpp.component.AbstractComponent.access$100(
at org.xmpp.component.AbstractComponent$
at java.util.concurrent.ThreadPoolExecutor.runWorker(
at java.util.concurrent.ThreadPoolExecutor$
Caused by: org.jivesoftware.smack.XMPPException$XMPPErrorException: XMPP error reply received from XMPPError: not-allowed - cancel
at org.jivesoftware.smack.XMPPException$XMPPErrorException.ifHasErrorThenThrow(
at org.jivesoftware.smack.StanzaCollector.nextResultOrThrow(
at org.jivesoftware.smack.StanzaCollector.nextResultOrThrow(
at org.jivesoftware.smackx.disco.ServiceDiscoveryManager.discoverInfo(
at org.jivesoftware.smackx.disco.ServiceDiscoveryManager.discoverInfo(
at org.jivesoftware.smackx.disco.ServiceDiscoveryManager.supportsFeatures(
at org.jivesoftware.smackx.disco.ServiceDiscoveryManager.supportsFeatures(
at org.jivesoftware.smackx.disco.ServiceDiscoveryManager.supportsFeature(
at org.jivesoftware.smackx.muc.MultiUserChatManager.providesMucService(
at org.jivesoftware.smackx.muc.MultiUserChat.enter(
at org.jivesoftware.smackx.muc.MultiUserChat.createOrJoin(
at org.jivesoftware.smackx.muc.MultiUserChat.createOrJoin(
at org.jitsi.impl.protocol.xmpp.ChatRoomImpl.joinAs(
… 17 more


It appears the conference mapper isn’t running in your prosody then. The purpose of that module is to map to [test] If it’s not running or properly configured you’ll see the errors you see above. Likely the prosody logs show something about it failing to load or failing to be configured properly. The muc_domain_mapper module must be loaded for the conference muc component modules, and a global variable must be defined muc_mapper_domain_base. Check your prosody configuration and see if those are being properly configured.

I have configured it correctly now and it is working fine. Thanks!

skype - swatikrishna1

@Aaron_K_van_Meerten Hello , I have a quick question, Imagine I have a consumer who uses the multi domain Jitsi platform , and there is another consumer uses the same in parallels. What is the best method to differentiate these two customer based on JWT token. I see there is only on app ID and secret can be configured in docker , keeping that in mind , can you suggest a method to remove a consumer when the contract expires. I cannot change the existing APP id and secret since it is being used by others too.

Thanks you for your help

@damencho @saghul

Swathi krishna

We use the ‘sub’ field in this case to distinguish the tenant. So, if you set the ‘sub’ to something other than ‘’, then the JWT will only be useable for meetings whos tenant URL matches the value in ‘sub’. The other option is to filter the room name, if the ‘room’ value in the JWT has anything other than '’, then the JWT is only good for that room. If you set both ‘sub’ and ‘room’ then the JWT is only good for that one room under that one tenant.

Hope this helps answer your question.

Yes it answered to some extend, but my question is , I have only one app id and secret is added in environment variables. This is same for both the tenant and what if I wanted to remove one tenant and keep other tenant active. I cannot change the app id and secret if one tenant leaves because other tenant still uses it. Is there any other parameters can be added in configmap to differentiate the tenants and remove when they leaves?

Thank you for your time

@Aaron_K_van_Meerten Please let me know if priv key pub key combination would be a solution here. Do you have some procedure for it?