Local Jitsi Meet + Zrok

I had the idea today to spin up my own private Jitsi Meet and share it using zrok for easy private video calling.

It was really easy to get Jitsi up and running locally using docker compose by following these instructions. I used the default ports 8000 and 8443.

I then I tried zrok share public localhost:8000 and it almost worked. I was able to get the Jitsi Meet main page at http://localhost:8000 and https://localhost:8443. I was also able to get to the main page using https://<TOKEN>.share.zrok.io, but when I attempted to join a call I got the following error from Jitsi

I did see this note in the Self-Hosted Jitsi Meet instructions,

So, probably I should rather share HTTPS rather than HTTP. I did try that, i.e. zrok share public https://localhost:8443, but nothing seemed to work at all - I got an error like this in the zrok console,

β”‚[  31.889]   ERROR zrok/endpoints/proxy.newReverseProxy.func2: error proxying: tls: failed to verify certificate: x509: certificate relies on legacy Common  β”‚
β”‚Name field, use SANs instead

I'm woefully underequipped to work this out. Of course I don't expect anyone here to be a Jitsi expert, but maybe someone has got a few hints that could guide me here. Or maybe someone has experience with zrok + another video calling stack that I could try as an alternative.

Thanks in advance!

Hi @stefanadelbert,

I'm not a Jitsi expert. I set it up following those instructions and I can join the meeting and got the same error as you...

I opened dev tools BEFORE joining the meeting (otherwise I saw nothing) and then I had a hint:

strophe.stream-management.js:224 WebSocket connection to 'wss://localhost:8443/xmpp-websocket?room=automaticenvironmentsaccelerateevenly' failed: 

This led me to finding the PUBLIC_URL setting for jitsi... Since it wants a reliable URL I then needed to use zrok reserve to make a share that never changes:

zrok reserve public https://localhost:8443 --unique-name  nameThisWhatever

With a reserved share, i could then share it:

zrok share reserved nameThisWhatever

With a reserved share I could update the jitsi .env file:

PUBLIC_URL=https://nameThisWhatever.clint.demo.openziti.org:8445

The key is to share that https url with --insecure such as:

zrok share reserved nameThisWhatever --insecure --headless

Now unfortunately, when I did that and when I'd gotten by all the hurdles, I still ran into what I think might be a zrok issue protocol error: received DATA before a HEADERS frame:

[   4.165]    INFO main.(*shareReservedCommand).run: [] -> GET /structuralhierarchieswrapyesterday
[   5.276]    INFO main.(*shareReservedCommand).run: [] -> GET /libs/excalidraw-assets/vendor-75e22c20f1d603abdfc9.js
[   7.755]    INFO main.(*shareReservedCommand).run: [] -> GET /xmpp-websocket?room=structuralhierarchieswrapyesterday
2024/07/31 12:20:39.794 INFO    protocol error: received DATA before a HEADERS frame
[   7.757]   ERROR zrok/endpoints/proxy.newReverseProxy.func2: error proxying: stream error: stream ID 5; PROTOCOL_ERROR
[   7.766]    INFO main.(*shareReservedCommand).run: [] -> GET /pwa-worker.js

I'll see if @michael.quigley has any thoughts as to what's going on here. It feels like there's just a bug that needs to be squashed.... It looks like it'll work though once this is settled...

Jitsi relies on multiple ports, including UDP transport, IIUC.

  • 80 TCP => For SSL certificate verification / renewal with Let's Encrypt. Required
  • 443 TCP => For general access to Jitsi Meet. Required
  • 10000 UDP => For General Network Audio/Video Meetings. Required
  • 22 TCP => For Accessing your Server using SSH (change the port accordingly if it's not 22). Required
  • 3478 UDP => For querying the stun server (coturn, optional, needs config.js change to enable it).
  • 5349 TCP => For fallback network video/audio communications over TCP (when UDP is blocked for example), served by coturn. Required

from Self-Hosting Guide - Debian/Ubuntu server | Jitsi Meet

EDIT: The Docker guide shows only one UDP port for RTP.

Thanks for the quick replies, guys.

I followed @TheLumberjack 's instructions and I was able to connect using the insecure HTTPS reserved zrok share! I was able to have video call with myself between laptop (hosting the self-hosted Jitsi) and my phone (from a browser and also using the Jitsi app). The conversation was one-sided... But it worked!

I'm not sure of the implications of the other ports as mentioned by @qrkourier. The Jitsi docker compose file does expose a few ports to the host, namely,

  • $HTTP_PORT TCP (8000)
  • $HTTPS_PORT TCP (8433)
  • $JICOFO_REST_PORT TCP (8888)
  • $JVB_COLIBRI_PORT TCP (8080)
  • $JVB_PORT UDP (10000)

Perhaps those other ports (22, 3478, 5349) are required for the different components to speak to each other, i.e. internal, but don't need to exposed externally.

Jocifi and JVB are for some other features like recording video I think, etc. I'll have another look tomorrow.

3 Likes

That's great news! I think it means the Jitsi Video Bridge (JVB) will fall back to multiplexing WebRTC via the web server's TCP port.

1 Like

@qrkourier So awesome that you already have a guide for this! Just busy following your instructions. I'm getting

❯ bash compose.bash logs zrok-enable
zrok-enable-1  | ERROR: STATE_DIRECTORY is undefined. This script must be run from systemd because it runs as a dynamically-allocated user and exclusively manages the files in STATE_DIRECTORY

I see this in zrok-enable.bash (zrok/nfpm/zrok-share.bash at bf7b8ecb999f2ad5b552b1032eb1e71914a4a1b9 Β· openziti/zrok Β· GitHub),

# set HOME to the first colon-sep dir in STATE_DIRECTORY inherited from systemd, e.g. /var/lib/zrok-share
if [[ -n "${STATE_DIRECTORY:-}" ]]; then
  export HOME="${STATE_DIRECTORY%:*}"
else
  echo "WARNING: STATE_DIRECTORY is undefined. Using HOME=${HOME}" >&2
fi
echo "DEBUG: zrok state directory is ${HOME}/.zrok"

This can't be what's causing the error, but maybe I'm looking at a newer version of that code than is in the image i'm pulling (openziti/zrok latest debc0efa2ff1 2 months ago 558MB).

Should I be setting STATE_DIRECTORY?

Indeed, there was a code change. See closed permission mode for Linux and Docker private shares Β· openziti/zrok@4722396 Β· GitHub.

It was my fault. Pulling the latest openziti/zrok image solved my problem.

❯ docker pull openziti/zrok:latest
latest: Pulling from openziti/zrok
e394ea8406c7: Pull complete
a523d2fee3c5: Pull complete
33bf11bd948c: Pull complete
db65b4ee270b: Pull complete
5b891b464d4d: Pull complete
0109b9c9dd29: Pull complete
4f4fb700ef54: Pull complete
5ed6ab3a490b: Pull complete
0bfcf7864963: Pull complete
4ec9d1d70b47: Pull complete
fa5abe65c395: Pull complete
a244d7047071: Pull complete
645ea4e82e4f: Pull complete
40802f2afba5: Pull complete
Digest: sha256:645b2cf2bd1cd3d421f4980ebaef7aafb6785c107425945f9f8686a82f302bf9
Status: Downloaded newer image for openziti/zrok:latest
docker.io/openziti/zrok:latest

dev/jitsi/self-hosted on ☁️  stefan.adelbert@lassio.io(australia-southeast1) took 29s
❯ docker image ls -a | grep zrok
openziti/zrok                                                                                     latest                    2c952158044e   16 hours ago    588MB

❯ bash compose.bash up
[+] Running 8/8
 βœ” Container self-hosted-zrok-test-1    Recreated                                                                                                                                                                                                                                                                          0.4s
 βœ” Container self-hosted-zrok-init-1    Created                                                                                                                                                                                                                                                                            0.0s
 βœ” Container self-hosted-prosody-1      Running                                                                                                                                                                                                                                                                            0.0s
 βœ” Container self-hosted-jvb-1          Running                                                                                                                                                                                                                                                                            0.0s
 βœ” Container self-hosted-zrok-enable-1  Recreated                                                                                                                                                                                                                                                                          0.1s
 βœ” Container self-hosted-jicofo-1       Running                                                                                                                                                                                                                                                                            0.0s
 βœ” Container self-hosted-web-1          Running                                                                                                                                                                                                                                                                            0.0s
 βœ” Container self-hosted-zrok-share-1   Recreated                                                                                                                                                                                                                                                                          0.1s
Attaching to jicofo-1, jvb-1, prosody-1, web-1, zrok-enable-1, zrok-init-1, zrok-share-1, zrok-test-1
zrok-init-1    | changed ownership of '/mnt/' to 2171:2171
zrok-init-1 exited with code 0
zrok-enable-1  | WARNING: STATE_DIRECTORY is undefined. Using HOME=/mnt
zrok-enable-1  | DEBUG: zrok state directory is /mnt/.zrok
zrok-enable-1  | INFO: reading enable parameters from environment variables
zrok-enable-1  | zrok configuration updated
zrok-enable-1  | INFO: running: zrok enable ...
zrok-enable-1  | {"file":"/home/runner/work/zrok/zrok/cmd/zrok/enable.go:102","func":"main.(*enableCommand).run","level":"info","msg":"contacting the zrok service...","time":"2024-08-01T10:37:54.955Z"}
zrok-enable-1  | {"file":"/home/runner/work/zrok/zrok/cmd/zrok/enable.go:157","func":"main.(*enableCommand).run","level":"info","msg":"the zrok environment was successfully enabled...","time":"2024-08-01T10:37:57.106Z"}
zrok-enable-1 exited with code 0

:thumbs_up:

1 Like

I constantly forget to pull too. It would be helpful if compose had an equivalent of Kubernetes image pull policy.

1 Like

I take it always doesn't work for some reason?

1 Like

I'm now getting carried away with building my own customised jitsi/web image with my own logos and theme. Making the changes and rebuilding the frontend is easy enough. But then building that into the equivalent of the jisti/web image, is a bridge too far for tonight...

1 Like

Oh - Thumbs Up! I've just added pull_policy: always and I can see that compose has pulled for those services.

❯ bash compose.bash up
[+] Running 3/3
 βœ” zrok-share Pulled                                                                                                                                                                                                                                                                                                       2.8s
 βœ” zrok-enable Pulled                                                                                                                                                                                                                                                                                                      2.8s
 βœ” zrok-test Pulled 

I'm learning a lot on this discourse. :pray:

Thanks @TheLumberjack! I didn't realize compose does have a pull policy! I see the default pull policy is "missing," so it's not pulled when there's a newer image, only when no images matching the spec have been downloaded. Nice.