One way media only (NAT issues) after upgrade

Nope, you are still advertising the old turn:

Did you make sure your turn server uses port 443?
Is it using a valid cert for the advertised hostname (
Did you modify prosody config to include turn and turns using port 443?
Did you restart prosody?

To test just open chrome://webrtc-internals and then open two tabs, you should have similar to my screenshot above.

Sorry for not being clear, but I am testing in a replica environment… This time Jitsi Meet is running on and the turn server is on

You can test like this:

Certificates are valid (generated with letsencrypt and seem fine). I was able to use TURN properly on some networks setups. The problem only occurs on certain network conditions of our customer.

Prosody configs:

    turncredentials_secret = "XXX";
    turncredentials_host = "";
    turncredentials_port = 443;

I checked the configs in and everything seemed fine.

I did change the config in prosody as well as did a restart. Any other hints?

Had you enabled the module turncredentials?
Didn’t you have the version of turncredentials module that supports multiple turn servers, I think you do cause the other server was using multiple turn servers

turncredentials_secret = "XXX";
turncredentials = {
  { type = "stun", host = "", port = 443 },
  { type = "turn", host = "", port = 443, transport = "udp" },
  { type = "turns", host = "", port = 443, transport = "tcp" }

Verify the use of turn servers using webrtc-internals page.

Where exactly do I configure this? I was not able to find the right documentation. Should be be directly in /usr/lib/prosody/modules/mod_turncredentials.lua or /etc/prosody/conf.d/ ?

I tried both but with no luck… I am currently using a single Turn server… Here is my /usr/lib/prosody/modules/mod_turncredentials.lua contents:

-- XEP-0215 implementation for time-limited turn credentials
-- Copyright (C) 2012-2013 Philipp Hancke
-- This file is MIT/X11 licensed.

local st = require "util.stanza";
local hmac_sha1 = require "util.hashes".hmac_sha1;
local base64 = require "util.encodings".base64;
local os_time = os.time;
local secret = module:get_option_string("turncredentials_secret");
local host = module:get_option_string("turncredentials_host"); -- use ip addresses here to avoid further dns lookup latency
local port = module:get_option_number("turncredentials_port", 3478);
local ttl = module:get_option_number("turncredentials_ttl", 86400);
if not (secret and host) then
    module:log("error", "turncredentials not configured");


module:hook("iq-get/host/urn:xmpp:extdisco:1:services", function(event)
    local origin, stanza = event.origin, event.stanza;
    if origin.type ~= "c2s" then
    local now = os_time() + ttl;
    local userpart = tostring(now);
    local nonce = base64.encode(hmac_sha1(secret, tostring(userpart), false));
    origin.send(st.reply(stanza):tag("services", {xmlns = "urn:xmpp:extdisco:1"})
        :tag("service", { type = "stun", host = host, port = port }):up()
        :tag("service", { type = "turn", host = host, port = port, username = userpart, password = nonce, ttl = ttl}):up()
    return true;

This is what I see:

What I noticed is that only the first party joining receives that. For instance, if I am in the room and someone joins, I can see that log above. If it’s the other way around (i join a room with someone there) the iceServers comes empty. I think you can easily test this for yourself.

So you have enabled useStunTurn: true in the main part of config.js, this will set turn servers to the bridge connection, but only those that are turns will be added there. So you are seeing it empty on the bridge peer connection and you see all of them on the p2p peer connection.

The version of turn credentials we are using should be this:

I cannot find it online, but the one above should be fine, maybe we will commit our version in jitsi-meet repository at some point.
The one you are using is using only one host from turncredentials_host, where the other one is getting multiple hosts: local hosts = module:get_option("turncredentials") or {};

The configuration goes in /etc/prosody/conf.d/
But needs to in the general part, not under some virtual host.

Apparently with the new config file you sent, things seem to be working. Are the logs files as expected now?


At least I can see something inside iceServer in both ways.

However, I don’t understand why sometimes it still opts for the bridge and not the TURN server or P2P given that it’s just 2 people. Is there a case where this could / should occur?

What do you mean?

You are asking can it be that 2 people will be connected over the bridge connection even if p2p is enabled? If you have a turn server for the p2p connection I doubt it will use the jvb connection. But generally the idea is switching between both if one is not working, using the other one … If you don’t have turn on the p2p connection than it is very likely in some network conditions to use the jvb connection.

Yes, that was the question. I do have P2P enabled and TURN configured. However, in some cases (for example, testing 2 browsers from my computer - I know it’s not the best scenario but also not the only one) it still ends up connecting with the JVB.

Regarding the point where you mention

If you don’t have turn on the p2p connection than it is very likely in some network conditions to use the jvb connection.

Does this mean I have to add the TURN server in the config.js as well? Or is it enough at the prosody configs? Today this is what I have (I assume it is similar to yours)

p2p: {

  enabled: true,

  useStunTurn: true,

  stunServers: [
    { urls: '' },
    { urls: '' },
    { urls: '' }

  preferH264: true,
  disableH264: true


As for the initial problem, I will confirm tomorrow weather it was fixed as my customer is not available right now to test the new settings / setup.

Yep those stun/turn/turns you configured in prosody will be used and for the p2p connection. Hum it is strange for me to open two tabs in the same browser and that one to use jvb connection, it should be p2p.

In fact it was 2 different browsers within the same computer (still, should be P2P). Here is a screenshot of that situation:


You can test for yourself (if you have the time or the curiosity to check) in (see the IP: is the same as the JVB)

Is one of the browsers Firefox? Firefox p2p in certain cases doesn’t work (Firefox cannot be offerer, the offerer is randomly chosen), so it will work randomly :slight_smile:

Well, one of them was firefox :slight_smile:

As for the problem we were facing, looks like it has been solved. Will report back if more users complain over the next hours / days.

Thanks once again for the detailed explanations.

@damencho I’ve been analyzing this file: (lines 47 - 65). That code block basically generates the one-time username and password to be used in the TURN server. Now, the part I don’t understand is if we use CoTURN server, how do the generated username and password get accepted by CoTURN?

There is a shared secret (turncredentials_secret) configured in both.

@damencho: right,I understand there’s a shared secret. But what confuses me is how the username generation on the Prosody gets accepted by the coTurn server. This is how Prosody generates the username:

and this is how coTurn generates its username:

They are different. As such the Prosody username shouldn’t work at all, but it does.

You can find more information here: