Running jibri locally against a docker-jitsi-meet deployment
This directory contains a script and a docker-compose override that let you run jibri from this source tree while the rest of the Jitsi stack (prosody, jicofo, jvb, web) runs in docker via docker-jitsi-meet. Useful for iterating on jibri code changes without rebuilding the docker image.
Files
run-jibri-local.sh— generatesjibri.conf+logging.propertiesfrom yourdocker-jitsi-meet/.env, builds jibri if needed, and launches it.prosody.override.yml— publishes prosody's port 5222 on127.0.0.1so the host-side jibri can reach it. The default compose onlyexpose:s this port inside the docker network.
Prerequisites
docker+docker composemvnand Java 17 (to build jibri)ffmpegonPATH(jibri shells out to it for capture)- A clone of docker-jitsi-meet
(defaults to
~/dev/docker-jitsi-meet; override withDOCKER_JITSI_DIR) - A
chromedriverbinary at the jibri repo root, matching your installed Chrome milestone (download from https://googlechromelabs.github.io/chrome-for-testing/)
Step by step
1. Set up docker-jitsi-meet
cd ~/dev/docker-jitsi-meet
cp env.example .env
./gen-passwords.sh
mkdir -p ~/.jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}
2. Edit .env: enable recording and set PUBLIC_URL
Two changes in ~/dev/docker-jitsi-meet/.env:
Enable recording. This is what tells prosody to provision the jibri
and recorder accounts (using JIBRI_XMPP_PASSWORD /
JIBRI_RECORDER_PASSWORD from gen-passwords.sh) and what makes the web
UI surface the record button. Without it, jibri can't even log into
prosody.
ENABLE_RECORDING=true
Point PUBLIC_URL at your LAN IP. Jibri opens the conference URL in
Chrome from the host. localhost won't do, because Chrome's WebRTC needs
an address that resolves the same way for jibri and the other browsers
joining the call. Use the host's LAN address.
Find your local IP:
# macOS
ipconfig getifaddr en0
# Linux
hostname -I | awk '{print $1}'
Then set (HTTP/HTTPS port already exist in env.example — adjust if you moved them):
HTTP_PORT=8000
HTTPS_PORT=8443
PUBLIC_URL=https://192.168.1.42:8443 # ← your IP and the HTTPS_PORT
The script copies PUBLIC_URL into jibri's base-url, so jibri opens
https://192.168.1.42:8443/<room> instead of the default https://meet.jitsi/.
3. Bring the stack up with prosody 5222 published
The host-side jibri talks to prosody over XMPP, so we need 5222 reachable from the host. Apply the override from this directory:
cd ~/dev/docker-jitsi-meet
docker compose \
-f docker-compose.yml \
-f ~/dev/jibri/doc/local-run/prosody.override.yml \
up -d
Confirm prosody is listening on the host:
nc -zv 127.0.0.1 5222 # → "succeeded!"
4. Build jibri
cd ~/dev/jibri
mvn -B -DskipTests clean package
The script will also build automatically if no jar is found in target/.
5. Drop a matching chromedriver next to the jibri repo
# look up your Chrome major version
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version
# fetch the matching chromedriver and place it at ~/dev/jibri/chromedriver
# (see https://googlechromelabs.github.io/chrome-for-testing/)
chmod +x ~/dev/jibri/chromedriver
6. (Linux only) Set up audio routing for ffmpeg
Jibri's default ffmpeg config captures audio from the ALSA PCM plug:bsnoop,
which is a custom loopback device defined in /etc/jitsi/jibri/asoundrc and
backed by the kernel snd-aloop module. The .deb postinst sets this up for
the system jibri user; if you're running locally as your own user, you
need to do it yourself, or switch to PulseAudio capture.
Option A — ALSA loopback (matches the deb):
# 1. Load the kernel loopback module (creates ALSA card "Loopback")
sudo modprobe snd-aloop
# Persist across reboots
echo snd-aloop | sudo tee /etc/modules-load.d/snd-aloop.conf
# 2. Drop jibri's asoundrc as your user's ALSA config
cp ~/dev/jibri/resources/debian-package/etc/jitsi/jibri/asoundrc ~/.asoundrc
# Verify
aplay -L | grep bsnoop
arecord -D plug:bsnoop -f cd -d 1 /tmp/t.wav && echo OK
Caveat: this changes your default ALSA device to a loopback, and on a
modern Ubuntu desktop Chrome routes audio through PulseAudio/PipeWire — so
even with bsnoop defined, Chrome won't play into the loopback unless you
also force Chrome to ALSA (e.g. PULSE_SERVER=/dev/null).
Option B — PulseAudio capture (recommended on a desktop dev box):
Skip the asoundrc/snd-aloop setup entirely and override the audio knobs when invoking the script:
JIBRI_AUDIO_SOURCE=pulse \
JIBRI_AUDIO_DEVICE=default \
~/dev/jibri/doc/local-run/run-jibri-local.sh
default reads from Pulse's default source. To capture system audio
specifically, find the right monitor source with pactl list short sources
and pass it as JIBRI_AUDIO_DEVICE (e.g. alsa_output.pci-…analog-stereo.monitor).
6b. (macOS only) Capture the screen instead of the camera
Jibri's default macOS ffmpeg input is -i 0:0 — whatever avfoundation
device 0 is (usually a camera + the mic). To record the conference you
want a screen device for video and a loopback device for audio.
-
Grant Screen Recording permission to the terminal app you launch the script from (System Settings → Privacy & Security → Screen Recording), then fully quit and relaunch the terminal. Without it avfoundation silently produces black frames.
-
Install a loopback audio device, e.g. BlackHole (
brew install blackhole-2ch), and route the call audio into it: Audio MIDI Setup →+→ Create Multi-Output Device → check both BlackHole and your speakers, then select that multi-output device as the system sound output. (Selecting BlackHole alone also works, but you won't hear anything.) -
Find the device indices:
ffmpeg -f avfoundation -list_devices true -i "" # e.g. video: [2] Capture screen 0 audio: [2] BlackHole 2ch -
Run with the indices:
JIBRI_MAC_VIDEO_DEVICE=2 JIBRI_MAC_AUDIO_DEVICE=2 \ ~/dev/jibri/doc/local-run/run-jibri-local.sh
Note avfoundation screen devices capture the whole display at native
(retina) resolution — there is no per-window capture in ffmpeg on macOS.
The script scales the output down to JIBRI_RECORDING_RESOLUTION width,
but whatever else is on that screen ends up in the recording, so keep the
jibri Chrome window in front (or point it at a secondary display, e.g.
Capture screen 1).
7. Run jibri
~/dev/jibri/doc/local-run/run-jibri-local.sh
You should see:
config: /Users/you/dev/jibri/.local/jibri.conf
jar: /Users/you/dev/jibri/target/jibri-*-jar-with-dependencies.jar
chromedriver: /Users/you/dev/jibri/chromedriver
xmpp: 127.0.0.1:5222 (domain=meet.jitsi)
… INFO: Jibri starting up …
… INFO: Connecting to xmpp environment on 127.0.0.1 …
… INFO: Joined MUC: jibribrewery@internal-muc.meet.jitsi
Now open https://192.168.1.42:8443/<room> in two browsers, click the
record button — jibri will pick up the start IQ and join the call.
Where things land
Generated files live under ~/dev/jibri/.local/:
| File | Purpose |
|---|---|
jibri.conf |
HOCON config built from .env |
logging.properties |
JUL config (console + ffmpeg/browser file handlers) |
recordings/<session-id>/*.mp4 |
recorded files |
ffmpeg.0.txt |
ffmpeg stderr (JUL captures it; not on console) |
browser.0.txt |
chrome browser logs |
chromedriver.log |
chromedriver's own log |
Configuration knobs
All overridable as env vars when invoking the script:
| Var | Default | Notes |
|---|---|---|
JIBRI_DIR |
resolved from script path | jibri repo root |
DOCKER_JITSI_DIR |
$HOME/dev/docker-jitsi-meet |
source of .env |
XMPP_HOST |
127.0.0.1 |
where prosody is reachable |
XMPP_PORT |
5222 |
matches .env if set there |
JIBRI_INSTANCE_ID |
jibri-local-$$ |
shows up as MUC nickname |
CHROMEDRIVER |
$JIBRI_DIR/chromedriver |
path to chromedriver binary |
JIBRI_AUDIO_SOURCE |
alsa |
Linux: ffmpeg -f for audio (alsa, pulse) |
JIBRI_AUDIO_DEVICE |
plug:bsnoop |
Linux: ffmpeg audio input device (e.g. default for pulse) |
JIBRI_MAC_VIDEO_DEVICE |
unset | macOS: avfoundation video index (use a Capture screen N device) |
JIBRI_MAC_AUDIO_DEVICE |
unset | macOS: avfoundation audio index (use a loopback device, e.g. BlackHole) |
Troubleshooting
permission denied while trying to connect to the docker API at unix:///var/run/docker.sock
— Linux only; your user isn't in the docker group. Fix:
sudo usermod -aG docker $USER
newgrp docker # apply the new group in this shell, or log out/in
docker ps # verify
Avoid sudo docker compose … — files written to ~/.jitsi-meet-cfg end up
root-owned and bite you later.
cannot reach prosody at 127.0.0.1:5222 — you brought up the stack
without prosody.override.yml. Re-run step 3.
This version of ChromeDriver only supports Chrome version N — your
chromedriver and Chrome milestones don't match. Re-fetch chromedriver for
your Chrome version (step 5).
Jibri opens https://meet.jitsi/<room> instead of your IP — PUBLIC_URL
isn't set in .env, or you didn't restart the script after editing .env.
No such file or directory: /var/log/jitsi/jibri/chromedriver.log —
older jibri pinned that path unconditionally. Pull a tree where
webdriver.chrome.logfile is overridable, or mkdir -p the path with
write permissions.
ffmpeg quits with Unknown PCM bsnoop / cannot open audio device plug:bsnoop
on Linux — you don't have the ALSA loopback set up. Either follow step 6
Option A (snd-aloop + ~/.asoundrc) or skip ALSA and use Pulse via
JIBRI_AUDIO_SOURCE=pulse JIBRI_AUDIO_DEVICE=default.
ffmpeg quits abruptly with "Error opening output files: Invalid argument"
on macOS — usually an avfoundation arg conflict. Inspect
~/dev/jibri/.local/ffmpeg.0.txt for the real ffmpeg stderr. Note that the
default macOS ffmpeg input is -i 0:0 (camera + system mic), so the
resulting mp4 records your camera, not the chrome window — see step 6b
for screen capture via JIBRI_MAC_VIDEO_DEVICE/JIBRI_MAC_AUDIO_DEVICE.
Black video on macOS — the terminal app that launched the script lacks
the Camera (default 0:0 input) or Screen Recording (screen-capture input)
permission. avfoundation doesn't error on a denied device; it delivers
black frames. Grant the permission in System Settings → Privacy & Security
and fully restart the terminal app.
Black video on Linux — you're on a Wayland session
(echo $XDG_SESSION_TYPE), where x11grab cannot see the screen and
records black. Either log into an Xorg session, or run a dedicated virtual
display and point both Chrome and ffmpeg at it:
Xvfb :1 -screen 0 1280x720x24 &
DISPLAY=:1 ~/dev/jibri/doc/local-run/run-jibri-local.sh
(the second part — overriding ffmpeg's hardcoded -i :0.0+0,0 grab target —
currently requires editing input-linux in the generated conf).