Jitsi
Table of Contents
Jitsi
Architecture
Architecture (ref):
jitsi meet - static JS app (web thing)
jvb - streams bridge / router, mix traffic between users (main thing)
jicofo - manage sessions & load balancer
prosody - xmpp server, used for signalling (required)
jigasi - SIP gateway (optional)
jibri - broadcasting service (optional)
etherpad - for docs sharing (optional)
JVB_TCP_HARVESTER_DISABLED set to true ‘won’t help people with poor connections but people behind spartan firewalls’ (ref).
jvb seems like could be a replacement for TURN (ref): ‘If the network equipment uses deep packet inspection your only shot is a turnserver, jvb does not support a standard https link for the TCP fallback, and anyway coturn handling TCP traffic performs better than jvb.’
got a confirmation: ‘As jitsi-videobridge received TCP support, turn server is not needed anymore, as it was used to take the participants who has problems using UDP.’
jvb/videobridge can route traffic over TCP only, but it’s not recommended, as handshake is easily recognizeable.
WebSockets are possible instead of WebRTC (but not recommended, though)
Overall it seems like the best configuration - it’s TCP only configuration with public available TURN services.
On videobridge (ref): ‘Jitsi Videobridge can run behind a NAT, provided that both required ports are routed (forwarded) to the machine that it runs on. By default these ports are TCP/4443 and UDP/10000. If you do not route these two ports, Jitsi Meet will only work with video for two people, breaking upon 3 or more people trying to show video.‘
System requirements
Hetzner recommends CPX21 for Jitsi - it’s 3vCPUs 4GB RAM 80GB SSD configuration for 7EUR.
JVB & turn-server
As per:
The turnserver is needed in two cases. When a participant cannot use UDP, it will fallback to using TCP for the media via the turnserver. And the second one is to offload traffic from jvb in p2p calls when participants are not able to establish a direct connection, they need a relay - jvb and turnserver are such relays, if turn is not available the jvb will be used, but this only in case when direct p2p does not succeed.
How to change STUN server
As per:
It’s a template using a gotemplate-like syntax: The file you want is to review is probably: https://github.com/jitsi/docker-jitsi-meet/blob/master/jvb/rootfs/defaults/jvb.conf Then the tpl command is run on every container startup: tpl /defaults/jvb.conf > /config/jvb.conf
For tpl syntax you can see the project here: https://github.com/jitsi/tpl You can also see plenty of examples in the defaults/ directories in this repo.
Docker setup
curl https://get.docker.com/ | shsudo apt install unzipmkdir jitsiwget $(curl -s https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest | grep 'zip' | cut -d\" -f4)unzip stable-*cd jitsi-docker-jitsi-meet-*cp env.example .env./gen-passwords.shmkdir -p ./data/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}cp docker-compose.yml .env ../cd ..vi .env # as per example belowdocker compose up -dThat seems to be a working .env file. That file and docker-compose are the only two files required.
TZ=Europe/BerlinPUBLIC_URL=https://jitsi.example.comJVB_ADVERTISE_IPS=<server-ip>JVB_STUN_SERVERS=stun.example.com:3478STUN_HOST=stun.example.comSTUN_PORT=3478TURN_HOST=stun.example.comTURN_PORT=5349TURN_TRANSPORT=tcpTURN_CREDENTIALS=<turn-secret>TURNS_HOST=stun.example.comTURNS_PORT=5349
ENABLE_CLOSE_PAGE=0ENABLE_PREJOIN_PAGE=0ENABLE_WELCOME_PAGE=0
JVB_ADVERTISE_PRIVATE_CANDIDATES=falseENABLE_P2P=false
CONFIG=./data# JVB port to be public facingJVB_PORT=10000# HTTP port to be proxied via CaddyHTTP_PORT=127.0.0.1:8000HTTPS_PORT=127.0.0.1:8443RESTART_POLICY=unless-stoppedENABLE_JAAS_COMPONENTS=0ENABLE_LETSENCRYPT=0# enable recording hereENABLE_RECORDING=1JIBRI_RECORDING_DIR=/config/recordingsJIBRI_FINALIZE_RECORDING_SCRIPT_PATH=/config/finalize.shXMPP_MUC_MODULES=muc_allowners # everyone is moderator -> to let anyone switch on recording
JICOFO_AUTH_PASSWORD=...JVB_AUTH_PASSWORD=...JIGASI_XMPP_PASSWORD=...JIBRI_RECORDER_PASSWORD=...JIBRI_XMPP_PASSWORD=...
#JITSI_IMAGE_VERSION=latestSetup Caddy:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update sudo apt install caddyvi /etc/caddy/Caddyfile
# Global(logging) { log { hostnames {args[0]} output file //var/log/caddy/{args[0]}.log }}
# This enables full logging of the credentials, useful in debugging Authorization header{ servers { log_credentials }}
meet.example.com { import logging meet.example.com reverse_proxy 127.0.0.1:8000}Jibri
wget https://raw.githubusercontent.com/jitsi/docker-jitsi-meet/refs/heads/master/jibri.ymltail -n +3 jibri.yml # add at to docker-compose.yml manuallydocker compose up -dAdditional parameters config is here. Recording is available as mp4 file at data/jibri/recordings (or smth like that)
proxy JVB via socat in docker
services: jitsi_jvb_udp: image: alpine/socat container_name: jitsi_jvb_udp command: "UDP4-LISTEN:10000,reuseaddr,fork UDP4:jitsi-server:10000" network_mode: "host" restart: unless-stoppedPorts:
- 8000 (HTTP) proxy by Caddy
- 10000 UDP - JVB, available for publicly
- 8080 TCP - not in use
- 8888 TCP - not in use
shell script
Nice docs are available here: https://jitsi.github.io/handbook/docs/intro
Prepare clean empty server, that will only do the Jitsi conference for the people.
This guide follows scripted installation as per this page.
Create A DNS records for this server, ideally: *.meet - this will hide the exact configured names. Wait for some time for the DNS records to propagate - the script verify via public DNS services.
[!NOTE] Add
resolver1.opendns.comserver to the firewall exception Otherwisedig -4 +short $TURN_HOST @resolver1.opendns.comwould fail for no good reason. It’s208.67.222.222as of 2023-07-09
apt update && apt upgrade -ywget -T 10 -O jitsi-base-installer https://raw.githubusercontent.com/jitsi-contrib/installers/main/jitsi-base/jitsi-base-installerexport JITSI_HOST=open.meet.domain.comexport TURN_HOST=turn.meet.domain.comhost $JITSI_HOST # test DNS is populatedbash jitsi-base-installerJitsi with password protection
Jitsi’s config files are at /etc/prosody (as per this page).
apt-get updateapt-get install wget
wget -T 10 -O jitsi-secure-installer https://raw.githubusercontent.com/jitsi-contrib/installers/main/jitsi-secure/jitsi-secure-installer
export JITSI_HOST=jitsi.yourdomain.comexport TURN_HOST=turn.yourdomain.com
bash jitsi-secure-installerprosodyctl register <USERNAME> <FQDN> <PASSWORD>Password will be requested for the administrator.
Jitsi updates
[!WARNING] Update likely to break the system Last time I did this (2023-12-08) - it did broke the whole Jitsi system.
apt list --installed | grep jitsi | grep video # >= 2.3.38apt list --installed | grep jicofo # >= 1.0-1050apt install jicofo jitsi-meet jitsi-meet-prosody jitsi-meet-turnserver jitsi-meet-web jitsi-meet-web-config jitsi-videobridge2Jitsi functional tests (torturer)
[!NOTE] Server resources requirements At least 2 CPU cores required. Otherwise tests will fail with timeout error.
Ref.
wget -O - https://raw.githubusercontent.com/alexander-potemkin/quickies/main/docker_ubuntu.sh | bashsudo apt install -y mavengit clone https://github.com/jitsi/jitsi-meet-torture.gitcd jitsi-meet-torture/doc/gridcp -r ../../resources .wget -P resources https://github.com/jitsi/jitsi-meet-torture/releases/download/example-video-source/FourPeople_1280x720_30.y4msudo docker build --build-arg VERSION=latest --build-arg BROWSER=chrome -t jitsi/selenium-standalone-chrome:latest .sudo docker build --build-arg VERSION=latest --build-arg BROWSER=firefox -t jitsi/selenium-standalone-firefox:latest .sudo docker build --build-arg VERSION=beta --build-arg BROWSER=firefox -t jitsi/selenium-standalone-firefox:beta .sudo docker build --build-arg VERSION=beta --build-arg BROWSER=chrome -t jitsi/selenium-standalone-chrome:beta .vi docker-compose-v3-dynamic-grid.yml # add 'restart: unless-stopped' to both images#run single testcd ../../sudo docker compose -f ./doc/grid/docker-compose-v3-dynamic-grid.yml up -decho "10 seconds timeout" && sleep 10 && sudo docker ps # check that the containers are not restartingmvn test -Djitsi-meet.instance.url=https://meet.example.com -Djitsi-meet.tests.toRun=AudioOnlyTest -Denable.headless=true \-Dweb.participant1.isRemote=true -Dweb.participant2.isRemote=true -Dweb.participant3.isRemote=true -Dweb.participant4.isRemote=true -Dweb.participant5.isRemote=true -Dweb.participant6.isRemote=true -Dweb.participant7.isRemote=true -Dweb.participant8.isRemote=true -Dweb.participant9.isRemote=true -Dweb.participant10.isRemote=true -Dweb.participant11.isRemote=true -Dweb.participant12.isRemote=true \-Dremote.resource.path=/usr/share/jitsi-meet-torture \-Dremote.address=http://localhost:4444/wd/hubvi scripts/malleus.shReplace MALLEUS_TESTS_TO_RUN=MalleusJitsificus (205 line as of 2024-02-02) with MALLEUS_TESTS_TO_RUN=AudioOnlyTest,MuteTest.
If more resources are available (4CPUs and 8Gb RAM at least was on the latest machine), the following tests could be executed: MALLEUS_TESTS_TO_RUN=AudioOnlyTest,BreakoutRoomsTest,DisableSelfViewTest,EtherpadTest,FakeDialInAudioTest,FollowMeTest,LockRoomTest,ModeratorTest,MuteTest,OneOnOneTest,SinglePortTest,SwitchVideoTest,UrlNormalisationTest.
vi ~/test_jitsi.sh:
#!/bin/bashSERVER_TO_TEST="https://meet.example.com"
# logs and flag filesJITSI_TEST_OUTPUT="/tmp/jitsi_test_`date +%Y_%m_%d_%H-%M`"JITSI_FAILS_FLAG_FILE="/root/jitsi.fails"
echo "Changing CWD"SCRIPTDIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && cd $SCRIPTDIR/jitsi-meet-torture
echo "Starting Selenium grid"docker compose -f ./doc/grid/docker-compose-v3-dynamic-grid.yml up -d
echo "Starting tests"./scripts/malleus.sh --conferences=1 --participants=12 --senders=12 --audio-senders=12 --duration=120 --room-name-prefix=hammertesting --hub-url=http://127.0.0.1:4444/wd/hub --instance-url=$SERVER_TO_TEST 1>$JITSI_TEST_OUTPUT 2>$JITSI_TEST_OUTPUT.err
echo "Processing tests results"# Check the exit status of the last commandif [ $? -ne 0 ]; then # Last command failed, so create the 'jitsi.fails' file touch $JITSI_FAILS_FLAG_FILE /root/matrix_dedicated_alert.sh "ERROR: Jitsi server $SERVER_TO_TEST **FAILED**!"else # Last command succeeded, so remove the 'jitsi.fails' file if it exists if [ -e $JITSI_FAILS_FLAG_FILE ]; then rm $JITSI_FAILS_FLAG_FILE /root/matrix_dedicated_alert.sh "OK: Jitsi server $SERVER_TO_TEST **RECOVERED**" fifi
echo "Stopping Selenium grid"docker ps --format "{{.Names}}" | sort | xargs --verbose --max-args=1 -- docker stopdocker compose -f ./doc/grid/docker-compose-v3-dynamic-grid.yml down
echo "Done."Another (newer script)
#!/bin/bashJITSI_TEST_OUTPUT="/tmp/jitsi_test_`date +%Y_%m_%d_%H-%M`"JITSI_TEST_SUITE_FILE="/tmp/TestSuite.fails.`date +%Y_%m_%d_%H-%M`"JITSI_FAILS_FLAG_FILE="/root/jitsi.fails"#su - torturer -c 'cd /home/torturer/jitsi-meet-torture/ && ./scripts/malleus.sh --conferences=1 --participants=4 --senders=1 --audio-senders=2 --duration=120 --room-name-prefix=hammertesting --hub-url=http://127.0.0.1:4444/wd/hub --instance-url=https://meet.example.com' 1>$JITSI_TEST_OUTPUT 2>$JITSI_TEST_OUTPUT.err
su - torturer -c 'cd /home/torturer/jitsi-meet-torture/ && ./scripts/malleus.sh --conferences=2 --participants=4 --senders=2 --audio-senders=4 --duration=120 --room-name-prefix=hammertesting --hub-url=http://127.0.0.1:4444/wd/hub --instance-url=https://meet.example.com' 1>$JITSI_TEST_OUTPUT 2>$JITSI_TEST_OUTPUT.err
# Check the exit status of the last commandif [ $? -ne 0 ]; then # Last command failed, so create the 'jitsi.fails' file touch $JITSI_FAILS_FLAG_FILE /root/matrix_dedicated_alert.sh "Main Jitsi server is failing!" cp /home/torturer/jitsi-meet-torture/target/surefire-reports/TestSuite.txt $JITSI_TEST_SUITE_FILEelse # Last command succeeded, so remove the 'jitsi.fails' file if it exists if [ -e $JITSI_FAILS_FLAG_FILE ]; then rm $JITSI_FAILS_FLAG_FILE /root/matrix_dedicated_alert.sh "Main Jitsi server NO longer failing (shall be Ok)" fificrontab entry - execute test every 2 hours at working hours:
0 8-20/2 * * * /root/jitsi_test.sh > /root/jitsi_test.logvi /root/matrix_dedicated_alert.sh:
#!/bin/bashHOST='DNS'ROOM_ID='!id:domain_name'TOKEN='<matrix-access-token>'
MESSAGE="<i><b>$1</b></i>"
/usr/bin/curl -XPOST -k -d "{\"msgtype\":\"m.text\", \"body\": \"\", \"format\": \"org.matrix.custom.html\", \"formatted_body\":\"$MESSAGE\"}" "https://$HOST/_matrix/client/r0/rooms/$ROOM_ID/send/m.room.message?access_token=$TOKEN"Note: This config is only for 4 people concurrent video call. You can raise the number by adding more NODE_MAX_INSTANCES & NODE_MAX_SESSION parameter, or add more node with same configuration (just copy-paste node2 and name it node3, 4, etc.).
[!NOTE] Close server with firewall! Close at least port 4444 from an outside world.
Archive
Docker - deprecated
wget -O - https://raw.githubusercontent.com/alexander-potemkin/quickies/main/docker_ubuntu.sh | bashsudo apt-get install docker-compose-plugin unzipsudo usermod -aG docker `whoami`JITSI_ARCHIVE=`curl -s https://api.github.com/repos/jitsi/docker-jitsi-meet/releases/latest | grep 'zip' | cut -d\" -f4`wget --output-document=$(basename "$JITSI_ARCHIVE").zip "$JITSI_ARCHIVE"unzip $(basename "$JITSI_ARCHIVE").zipcd jitsi-docker-jitsi-meet-*/cp env.example .env./gen-passwords.shmkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}vi .envdocker compose up -dJitsi shall be available at https://localhost:8443