Docker, Jigasi and 3CX; Jigasi line always busy!


I managed to connect my plain vanilla docker install of Jitsi/Jigasi to my 3CX. Jigasi is registered by 3CX as an extension.

I also configured the org.jitsi.jigasi.DEFAULT_JVB_ROOM_NAME= property.

I then create the conference room specified above and try to call in (by dialling the Jigasi extension) on my 3CX but I keep getting a busy signal.

Does anybody know what’s going on here?

Make sure you put the full jid for the roomname as in jigasi/ at b904c3cb5ee2cf722b89c8bc85de0df8e0cec374 · jitsi/jigasi · GitHub

Also, attach the logs from jigasi to be able to understand what is going on.

Sorry, if I am being a bit slow…

So, does this mean I can’t just invent some default room name (like “testroom”) but it has to be “siptest@conference.<<DOMAIN_BASE>>”?

Can I make up the <<DOMAIN_BASE>>-part of the name? Or does this have to be “my” domain? Say, for example, my jitsi installation’s FQDN is “” - would I put “” as the default room name?

And what would be the name of the room to create then? Would it be “conference” or “”?

Thanks for your help!

Yes, if you muc component is But for docker I think it is docker-jitsi-meet/env.example at cf904618107a4ead9a8477458ab612ab277ae4b5 · jitsi/docker-jitsi-meet · GitHub
So it will be

If you are asking about the link it will be

You can of course replace siptest with whatever… its just you need the full xmpp jid for the room name that corresponds to your deployment.

Still no dice.

So I put

And I opened “” (where “” is my real domain).

But I still get the busy signal upon calling the Jigasi extension.

Sorry, I really don’t know what I am doing. I just note that in the file the next line is:

org.jitsi.jigasi.MUC_SERVICE_ADDRESS=conference.ok (still the plain vanilla install property) - should this also be somehow aligned with the jid "

Yep, that doesn’t seem right… Maybe show your prosody config. Also attach jigasi logs, its hard to be guessing there can be million of reasons …

Prosody config I haven’t touched but here it is:


-- Prosody Example Configuration File
-- Information on configuring Prosody can be found on our
-- website at
-- Tip: You can check that the syntax of this file is correct
-- when you have finished by running: luac -p prosody.cfg.lua
-- If there are any errors, it will let you know what and where
-- they are, otherwise it will keep quiet.
-- The only thing left to do is rename this file to remove the .dist ending, and fill in the
-- blanks. Good luck, and happy Jabbering!

---------- Server-wide settings ----------
-- Settings in this section apply to the whole server and are the default settings
-- for any virtual hosts

-- This is a (by default, empty) list of accounts that are admins
-- for the server. Note that you must create the accounts separately
-- (see for info)
-- Example: admins = { "", "" }
admins = { }

-- Enable use of libevent for better performance under high load
-- For more information see:
--use_libevent = true;

-- This is the list of modules Prosody will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
-- Documentation on modules can be found at:
modules_enabled = {

        -- Generally required
                "roster"; -- Allow users to have a roster. Recommended ;)
                "saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
                "tls"; -- Add support for secure TLS on c2s/s2s connections
                "dialback"; -- s2s dialback support
                "disco"; -- Service discovery

        -- Not essential, but recommended
                "private"; -- Private XML storage (for room bookmarks, etc.)
                "vcard"; -- Allow users to set vCards
                "limits"; -- Enable bandwidth limiting for XMPP connections

        -- These are commented by default as they have a performance impact
                --"privacy"; -- Support privacy lists
                --"compression"; -- Stream compression (Debian: requires lua-zlib module to work)

        -- Nice to have
                "version"; -- Replies to server version requests
                "uptime"; -- Report how long server has been running
                "time"; -- Let others know the time here on this server
                "ping"; -- Replies to XMPP pings with pongs
                "pep"; -- Enables users to publish their mood, activity, playing music and more
                "register"; -- Allow users to register on this server using a client and change passwords

        -- Admin interfaces
                "admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
                --"admin_telnet"; -- Opens telnet console interface on localhost port 5582

        -- HTTP modules
                --"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
                --"http_files"; -- Serve static files from a directory over HTTP

        -- Other specific functionality
                "posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
                --"groups"; -- Shared roster support
                --"announce"; -- Send announcement to all online users
                --"welcome"; -- Welcome users who register accounts
                --"watchregistrations"; -- Alert admins of registrations
                --"motd"; -- Send a message to users when they log in
                --"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.

https_ports = { }

-- These modules are auto-loaded, but should you want
-- to disable them then uncomment them here:
modules_disabled = {
        -- "offline"; -- Store offline messages
        -- "c2s"; -- Handle client connections
        "s2s"; -- Handle server-to-server connections

-- Disable account creation by default, for security
-- For more information see
allow_registration = false;

daemonize = false;

-- Enable rate limits for incoming client and server connections
limits = {
  c2s = {
    rate = "10kb/s";
  s2sin = {
    rate = "30kb/s";

pidfile = "/config/data/";

-- Force clients to use encrypted connections? This option will
-- prevent clients from authenticating unless they are using encryption.

c2s_require_encryption = false

-- Force certificate authentication for server-to-server connections?
-- This provides ideal security, but requires servers you communicate
-- with to support encryption AND present valid, trusted certificates.
-- NOTE: Your version of LuaSec must support certificate verification!
-- For more information see

s2s_secure_auth = false

-- Many servers don't support encryption or have invalid or self-signed
-- certificates. You can list domains here that will not be required to
-- authenticate using certificates. They will be authenticated using DNS.

--s2s_insecure_domains = { "" }

-- Even if you leave s2s_secure_auth disabled, you can still require valid
-- certificates for some domains by specifying a list here.

--s2s_secure_domains = { "" }

-- Select the authentication backend to use. The 'internal' providers
-- use Prosody's configured data storage to store the authentication data.
-- To allow Prosody to offer secure authentication mechanisms to clients, the
-- default provider stores passwords in plaintext. If you do not trust your
-- server please see
-- for information about using the hashed backend.

authentication = "internal_hashed"

-- Select the storage backend to use. By default Prosody uses flat files
-- in its configured data directory, but it also supports more backends
-- through modules. An "sql" backend is included by default, but requires
-- additional dependencies. See for more info.

--storage = "sql" -- Default is "internal" (Debian: "sql" requires one of the
-- lua-dbi-sqlite3, lua-dbi-mysql or lua-dbi-postgresql packages to work)

-- For the "sql" backend, you can uncomment *one* of the below to configure:
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }

-- Logging configuration
-- For advanced logging see
-- Debian:
--  Logs info and higher to /var/log
--  Logs errors to syslog also
log = {
        { levels = {min = "info"}, to = "console"};

-- Enable use of native prosody 0.11 support for epoll over select
network_backend = "epoll";
-- Set the TCP backlog to 511 since the kernel rounds it up to the next power of 2: 512.
network_settings = {
  tcp_backlog = 511;

component_interface = { "*" }

data_path = "/config/data"

smacks_max_unacked_stanzas = 5;
smacks_hibernation_time = 60;
smacks_max_hibernated_sessions = 1;
smacks_max_old_sessions = 1;

Include "conf.d/*.cfg.lua"

And this seems to be the pertinent part from the jigasi logs:

Jigasi 2021-07-16 13:29:46.478 INFO: [63] org.jitsi.jigasi.SipGateway.incomingCallReceived().216 [ctx=16264421864761913778062] Incoming call received...

Jigasi 2021-07-16 13:29:47.539 WARNING: [65] [ctx=16264421864761913778062] No JVB room name provided in INVITE header

Jigasi 2021-07-16 13:29:47.550 INFO: [67] org.jitsi.jigasi.SipGatewaySession.handleCallState().1617 [ctx=16264421864761913778062] SIP call ended: CallPeerChangeEvent: type=CallPeerStatusChange Call for peer=Someguy <11@>;status=Failed

Jigasi 2021-07-16 13:29:47.551 INFO: [67] org.jitsi.jigasi.SipGatewaySession.sipCallEnded().648 [ctx=16264421864761913778062] Sip call ended: Call: id=162644218630590114971 peers=0

Jigasi 2021-07-16 13:29:47.552 SEVERE: [67] org.jitsi.jigasi.AbstractGateway.notifyCallEnded().121 [ctx=16264421864761913778062] Call resource not exists for session.

Jigasi 2021-07-16 13:29:47.553 INFO: [67] org.jitsi.jigasi.SipGatewaySession.peerStateChanged().1683 null SIP peer state: Failed

So Jigasi does complain that no room was specified in the Invite but that’s what the default room is for, right?

This is the main prosody config. The config for your deployment is in this folder ^

This means the config you put is not taken into account and your sip invite does not have any room custom header.
Are out setting the config using the docker env - JIGASI_SIP_DEFAULT_ROOM?
What settings jigasi read you can see at the beginning of the log file.

Sorry, here is the other prosody config (haven’t touched this one either):

~/.jitsi-meet-cfg/prosody/config/conf.d# cat jitsi-meet.cfg.lua
admins = {

unlimited_jids = {

plugin_paths = { "/prosody-plugins/", "/prosody-plugins-custom" }
http_default_host = "meet.jitsi"

consider_bosh_secure = true;

-- Deprecated in 0.12


cross_domain_websocket = { "","https://meet.jitsi" }
cross_domain_bosh = { "","https://meet.jitsi" }

VirtualHost "meet.jitsi"

    authentication = "token"
    app_id = ""
    app_secret = ""
    allow_empty_token = true

    ssl = {
        key = "/config/certs/meet.jitsi.key";
        certificate = "/config/certs/meet.jitsi.crt";
    modules_enabled = {
        "smacks"; -- XEP-0198: Stream Management

    main_muc = ""
    lobby_muc = ""
    muc_lobby_whitelist = { "" }

    speakerstats_component = ""
    conference_duration_component = ""

    c2s_require_encryption = false

VirtualHost ""
    ssl = {
        key = "/config/certs/";
        certificate = "/config/certs/";
    modules_enabled = {
    authentication = "internal_hashed"

VirtualHost ""
    modules_enabled = {
    authentication = "internal_hashed"

Component "" "muc"
    storage = "memory"
    modules_enabled = {
    restrict_room_creation = true
    muc_room_locking = false
    muc_room_default_public_jids = true

Component "" "muc"
    storage = "memory"
    modules_enabled = {
    muc_room_cache_size = 1000
    muc_room_locking = false
    muc_room_default_public_jids = true

Component "" "client_proxy"
    target_address = ""

Component "" "speakerstats_component"
    muc_component = ""

Component "" "conference_duration_component"
    muc_component = ""

Component "" "muc"
    storage = "memory"
    restrict_room_creation = true
    muc_room_locking = false
    muc_room_default_public_jids = true

I haven’t put JIGASI_SIP_DEFAULT_ROOM in the .env file (there does not seem to be a placeholder provided for it) but rather in the file - so that’s wrong then?

Can I put JIGASI_SIP_DEFAULT_ROOM just anywhere in the .env file?

And what about the next line “org.jitsi.jigasi.MUC_SERVICE_ADDRESS=conference.ok” (still the plain vanilla install property) - should this also be somehow aligned with the jid "

I don’t have a lot of experience with Docker, but I know it recreates the config files based on the env. So I suspect your changes are not taken into account when you start jigasi.


That based on your prosody config needs to be

And it also should be coming from env.

Okay, so I edited the .env file and added



was already coming from the .env file.

And I probably had edited the wrong file before. The one you pointed me to on Github does take over both .env properties. Or it should…

Because I am still getting the same error

No JVB room name provided in INVITE header

So, if I understood you correctly, this means that the info from the .env file is still not getting through…

(I did recreate the jigasi container - only that one - but that should be enough - or do I need to recreate prosody as well?)

Not sure, as I said I don’t have experience with docker. I know people sometimes delete the config folder so it can be recreated, but not sure whether you need to do that after touching env. file.

This docker stuff is driving me crazy!

So I think I understand how it works in principle (and please correct me, if I am wrong):

There is a file jigasi/rootfs/defaults/ which holds a “template configuration” which, when the container is started (and, possibly only, if there is no /jigasi/config/ is translated into the actual config file /jigasi/config/ by evaluating the .env file.

In my case the template config contains, among many others, the lines

{{ end }}

org.jitsi.jigasi.MUC_SERVICE_ADDRESS={{ .Env.XMPP_MUC_DOMAIN }}

Now, for some reason that I still fail to understand, the line


seems to be ignored in the translation.

The lines before and after are translated but this one line is ignored and never makes it into the actual config.

I thought that maybe there might be something wrong with the if statement, so I commented it out. Doesn’t help.

I thought that maybe there might be something wrong with some if statement before it, so I moved the line around within the file, directly underneath a line that makes it into the actual config file. Doesn’t help.

I deleted /config/ I restarted the container. I recreated the container. Nothing helps.

I DON’T GET IT :face_vomiting: something is really effed up here…

it seems that my assumption of how docker works was wrong:

I changed another line in what I thought was the “template config” but that change, too, is not translated into the actual config.

But where, then, is the “template” config (or am I getting this completely wrong and there is no “template config” at all???

Solved it - sort of:

The key was to use docker-compose to remove and bring everything up again. Apparently the template config translation process is triggered (or performed) by docker-compose.

(While the default room property now is translated into the actual config file, I am still lost when it comes to the template config: I had edited what I thought was the template config and moved the default room line someplace else. But after I ran docker-compose, the default room line showed up exactly where it should have been before I edited the template file. So, it seems that this is not the template. But what is?)

Thanks for your help!