How to Configure WebSocket in Jitsi Videobridge (JVB)

Written by: Bagus Facsi Aginsa
Published at: 02 Jun 2020


Jitsi clients need more than an audio/video stream to run a conference: they also exchange control messages with the videobridge, things like dominant speaker changes, video quality preferences, and connection stats. This control traffic flows over the bridge channel, and Jitsi’s recommended transport for it is a WebSocket (the old SCTP data channel is deprecated). When the bridge channel is broken, meetings “work” but degrade in confusing ways: no dominant speaker switching, wrong video quality selected, participants stuck on low resolution.

In this tutorial you will enable the Colibri WebSocket on Jitsi Videobridge using the modern jvb.conf (HOCON) format, put Nginx in front of it for TLS, and verify the whole path from the browser. This is mainly needed on multi-server setups where the videobridge runs on its own machine. On a standard single-server Debian install, the bundled Nginx config already proxies the WebSocket for you.


How the Colibri WebSocket Works

The videobridge exposes the WebSocket on a local Jetty HTTP server (port 9090 by default, plain HTTP). Browsers, however, connect with wss:// on port 443, and they will refuse a plain-ws connection from an HTTPS page. So the path looks like this:

                     wss://your.jvb.domain/colibri-ws/<server-id>/...
          _________              ____________
         |         |    wss     |            |    ws (9090)
 browser |  client |----------->|   Nginx    |---------------> JVB (Jetty)
         |_________|            |____________|

Two identifiers tie the chain together:

  • domain is the public hostname (and port) the client should connect to. The bridge advertises this to clients through Jicofo.
  • server-id is a unique string per bridge (like jvb-1). It becomes part of the WebSocket URL path, which is how one Nginx (or one domain) can route to multiple bridges. It must match between jvb.conf and the Nginx location.

Prerequisites

  • A working Jitsi Meet installation with the videobridge on its own server (see Install Jitsi Meet with Multi Server Configuration). The bridge must already connect to Jicofo
  • A DNS record for the bridge, e.g. your.jvb.domain, pointing at the JVB server
  • A valid SSL certificate for that domain. Self-signed will not work, browsers silently refuse wss to untrusted certs
  • Root or sudo access on the JVB server

Step 1: Enable the WebSocket in jvb.conf

Modern Jitsi Videobridge is configured in /etc/jitsi/videobridge/jvb.conf using the nested HOCON format. The old flat sip-communicator.properties style (org.jitsi.videobridge.rest.COLIBRI_WS_* lines) is deprecated. And if a setting exists in both files, jvb.conf wins, so editing the old file often does nothing.

Open the config:

sudo nano /etc/jitsi/videobridge/jvb.conf

Add (or extend) the videobridge block:

videobridge {
    http-servers {
        public {
            port = 9090
        }
    }
    websockets {
        enabled = true
        domain = "your.jvb.domain:443"
        tls = true
        server-id = "jvb-1"
    }
}

If your jvb.conf already has a videobridge { ... } block, merge these keys into it rather than adding a second block.

What each setting does:

  • http-servers.public.port is the local Jetty port serving the WebSocket. 9090 is conventional. Do not use 8080, that is the private server used for the bridge’s own REST/statistics API.
  • domain is the public address clients will connect to, including :443. This is what ends up in the wss:// URL on the browser side.
  • tls = true tells the bridge to advertise wss:// instead of ws://. TLS itself is terminated by Nginx, not by the bridge.
  • server-id is any unique string per bridge (jvb-1, media-1, a hash). With multiple bridges behind one domain, each needs its own ID.

Restart the bridge:

sudo systemctl restart jitsi-videobridge2

Confirm it is advertising the WebSocket URL:

grep 'wss' /var/log/jitsi/jvb.log

You should see a line containing wss://your.jvb.domain:443/colibri-ws/jvb-1. That is the URL clients will be told to use.


Step 2: Install Nginx on the JVB Server

The Jetty server speaks plain HTTP on 9090; Nginx terminates TLS on 443 and proxies through:

sudo apt update
sudo apt install nginx

Step 3: Configure the Nginx WebSocket Proxy

Create a server block for the bridge:

sudo nano /etc/nginx/sites-available/videobridge.conf

Add the following, adjusting the domain, certificate paths, and server ID:

server {
    listen 443 ssl;
    server_name your.jvb.domain;

    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_certificate /path/to/your.jvb.domain.crt;
    ssl_certificate_key /path/to/your.jvb.domain.key;

    location ~ ^/colibri-ws/jvb-1/(.*) {
        proxy_pass http://127.0.0.1:9090/colibri-ws/jvb-1/$1$is_args$args;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_read_timeout 300s;
        tcp_nodelay on;
    }
}

The Upgrade/Connection headers and proxy_http_version 1.1 are what make the WebSocket handshake survive the proxy. The general pattern is explained in depth in How to Configure Nginx as a WebSocket Reverse Proxy. The proxy_read_timeout keeps idle bridge channels from being cut off at Nginx’s 60-second default.

Enable the site and reload:

sudo ln -s /etc/nginx/sites-available/videobridge.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Make sure both ports are listening:

sudo ss -ntlp | grep -E '443|9090'

You should see Nginx on 443 and Java (the bridge) on 9090.


Step 4: Check the Client Configuration on the Main Server

On the server running Jitsi Meet (the web frontend), open the client config:

sudo nano /etc/jitsi/meet/<your-meet-domain>-config.js

On current Jitsi Meet versions, the WebSocket bridge channel is the default, so there is usually nothing to change. Just make sure nothing forces the deprecated transport: look for openBridgeChannel: set to 'datachannel' and remove or comment it. On older deployments, you enable it explicitly:

openBridgeChannel: 'websocket',

Step 5: Verify End to End

From the command line, hit the WebSocket path over HTTPS:

curl -i https://your.jvb.domain/colibri-ws/jvb-1/

Expect an HTTP error like 405 (method not allowed) from Jetty. That is success: the request traveled through Nginx to the bridge. A 404 from Nginx, a 502, or a TLS error means the chain is broken (see troubleshooting below).

From a real conference: open a meeting with two participants, then open the browser developer tools, go to the Network tab, and filter by WS. You should see a connection to wss://your.jvb.domain/colibri-ws/jvb-1/... with status 101 Switching Protocols, and frames flowing in the Messages tab.

If instead the console logs show repeated Channel closed / bridge channel errors and endless reconnect attempts, the client got the wss URL but cannot complete the handshake.


Common Problems and Troubleshooting

404 Not Found from Nginx.

The server-id in the URL does not match your location regex. The ID appears in three places (jvb.conf, the Nginx location, and the proxy_pass path) and all three must be identical.

502 Bad Gateway.

Nginx cannot reach Jetty. Check the bridge is actually listening on 9090 (sudo ss -ntlp | grep 9090) and that you did not put the public HTTP server on a different port than the one in proxy_pass.

You edited the config but nothing changed.

You probably edited sip-communicator.properties while jvb.conf has its own values. The HOCON file overrides the old one, so put the WebSocket settings in jvb.conf. The same trap applies to every other bridge setting; see Troubleshoot Jitsi for the full old-format vs new-format story.

Browser connects, then immediately drops, with a certificate error in the console.

The certificate on your.jvb.domain is self-signed, expired, or does not match the domain. Browsers give no certificate warning UI for WebSocket connections. They just fail. Use a certificate from a real CA (Let’s Encrypt is free).

Everything verifies, but things only break when a third participant joins.

Two-participant calls are peer-to-peer by default and never touch the bridge. Always test bridge features with three participants, or set p2p: { enabled: false } temporarily in the client config while testing.

Firewall blocks.

Port 443/tcp must be open to clients on the JVB server. If you run UFW, see Setup Firewall Using UFW on Ubuntu. Port 9090 should not be exposed publicly, it is plain HTTP and only Nginx needs it.


Best Practices

Use jvb.conf (HOCON), not sip-communicator.properties. The old format is deprecated, current documentation assumes HOCON, and mixed configs are the number one source of “my change did nothing” confusion.

One unique server-id per bridge. When you scale to multiple bridges, duplicate server IDs make WebSocket routing ambiguous. Name them predictably (jvb-1, jvb-2, …).

Keep Jetty local. Bind 9090 behind the firewall and let Nginx be the only public entry point. The Jetty server has no TLS and no auth.

Monitor the bridge channel, not just the media. A broken WebSocket does not stop calls, it silently degrades them. A quick grep 'wss' /var/log/jitsi/jvb.log after every upgrade catches regressions early.


Conclusion

You have enabled the Colibri WebSocket on Jitsi Videobridge with the modern jvb.conf format, proxied it through Nginx with proper upgrade headers and TLS, and verified the full path with curl (the friendly 405) and the browser’s network inspector.

If you are running more than one videobridge, the next steps are wiring them into one deployment: Install Jitsi Meet with Multi Server Configuration covers adding bridges, and Configure OCTO in Jitsi lets a single conference span multiple bridges. And when a bridge refuses to show up in Jicofo at all, Troubleshoot Jitsi walks through the brewery-room debugging process step by step.