Tutorial

Host Jitsi on your own VPS

Self-hosting8 min read6 steps

Jitsi Meet is an open source video conferencing solution that works directly in the browser, with no installation on the participant's side. Self-hosting it on a VPS gives you as many rooms as you need, with no time limit and no data collection. The deployment itself is quick — configuring the TURN server (coturn) is what causes most installations to fail: without it, video works on a local network but stops as soon as a participant is behind a corporate NAT or a carrier-grade NAT provider. This guide covers the full installation, advanced coturn configuration, video bridge monitoring, and troubleshooting of the most common errors.

Contents· Jitsi self-hosted vs Zoom: what actually changes1/9
  1. 01Jitsi self-hosted vs Zoom: what actually changes
  2. 02Concrete benefits of self-hosted Jitsi
  3. 03Concrete prerequisites before you start
  4. 04Step-by-step deployment: Docker, .env, coturn and first test
  5. 05Advanced configuration: recording, authentication and limits
  6. 06Hardening: fail2ban on the TURN service
  7. 07Troubleshooting: the four most common errors
  8. 08Monitoring: tracking bandwidth and participants
  9. 09Your Jitsi is running — manage bandwidth

Jitsi self-hosted vs Zoom: what actually changes

Consumer video conferencing services (Zoom, Google Meet, Teams) impose time limits on free plans, participant quotas, and keep your recordings on their servers. Self-hosted Jitsi Meet removes these constraints: rooms with no cap on participants, no meeting time limit, no account required to join a meeting, and video streams that stay on your infrastructure. This is particularly relevant for an organization concerned about the confidentiality of its meetings, a firm handling sensitive data, or anyone who wants to embed video calling in a third-party application (Jitsi provides a stable iframe API). Running costs come down to the VPS bill — no per-seat subscription, no software surcharge.

Concrete benefits of self-hosted Jitsi

  • Meetings with no time limit and no cap on the number of rooms — no timer cutting the call.
  • No account required for participants: a link is enough to join.
  • iframe API to embed video calling directly in your application or website.
  • Transport encryption (DTLS-SRTP) and full control over any recordings.
  • Complete customization: logo, colors, room domain name.
  • No telemetry sent to a third party — video streams never leave your VPS.

Concrete prerequisites before you start

Jitsi is sized by bandwidth, not CPU. For meetings up to 10 participants: 2 vCPU, 4 GB RAM minimum, generous outbound traffic (100 Mbps recommended). Above 20 simultaneous participants, plan for 8 GB RAM and monitor JVB network load. Three ports must be reachable from the internet: TCP 443 (HTTPS and TURN/TLS), TCP 80 (Let's Encrypt renewal), UDP 10000 (JVB media). Add TCP 3478 if you expose coturn on its native port. You need Docker 24+ and Compose V2, a domain name meet.yourdomain.com with an A record pointing to the VPS public IP, and a dedicated, stable public IP — a shared IP makes coturn configuration significantly harder.

Step-by-step deployment: Docker, .env, coturn and first test

  1. Fetch docker-jitsi-meet

    Clone the official repository: git clone https://github.com/jitsi/docker-jitsi-meet. Copy env.example to .env, then run ./gen-passwords.sh — this script generates all internal secrets (Prosody, Jicofo, JVB). Do not skip this step: default values are not secrets. The stable-11146-2 branch (August 2026) is the latest stable; avoid main in production — internal components change without notice.

  2. Set PUBLIC_URL and DOCKER_HOST_ADDRESS

    Two variables in .env are critical: PUBLIC_URL=https://meet.yourdomain.com (used by the web client to build room URLs) and DOCKER_HOST_ADDRESS=<VPS_PUBLIC_IP> (the address JVB announces to clients to establish the direct media connection). Without DOCKER_HOST_ADDRESS, JVB announces an internal Docker address and no external client can reach it.

  3. Enable Let's Encrypt in .env

    Add to .env: ENABLE_LETSENCRYPT=1, LETSENCRYPT_DOMAIN=meet.yourdomain.com, [email protected]. Verify that port 80 is open before starting — Let's Encrypt uses an HTTP-01 challenge that requires a response on that port. For automatic renewal, the docker-jitsi-meet web container calls certbot via an internal cron: verify the volume ~/.jitsi-meet-cfg/web/letsencrypt is mounted and persisted across restarts.

  4. Configure coturn (TURN server)

    This is the step most guides skim over and that causes installations to fail. In .env, enable: ENABLE_TURN=1, TURN_HOST=meet.yourdomain.com, TURN_PORT=443, TURN_TRANSPORT=tls. Port 443 over TLS crosses the most restrictive firewalls, which block non-standard ports but let through anything resembling HTTPS.

    Then add: TURN_CREDENTIALS=a_strong_secret, TURN_TLS_CERT=/etc/letsencrypt/live/meet.yourdomain.com/fullchain.pem, TURN_TLS_KEY=/etc/letsencrypt/live/meet.yourdomain.com/privkey.pem. If coturn runs in a separate container, mount these files as volumes.

    One often-missed detail: coturn blocks RFC1918 ranges (192.168.x.x, 10.x.x.x, 172.16.x.x) by default. If your VPS has an internal network interface in those ranges, add no-multicast-peers and verify that denied-peer-ip does not include the JVB address.

  5. Start the stack and check the JVB

    Run docker compose up -d. Wait 30 seconds, then: docker compose logs jvb 2>&1 | grep -E 'register|connected|error'. You should see a line showing that JVB registered with Jicofo. If you see Failed to register or Connection refused, Prosody is not ready yet — retry after a minute. Then verify coturn responds: nc -zv meet.yourdomain.com 443 (TCP) and nc -zvu meet.yourdomain.com 10000 (UDP).

  6. First video test from two different networks

    Open https://meet.yourdomain.com from your usual machine and a second device on a different network (a phone on mobile data, for example). If both participants see and hear each other, TURN is working. A test between two machines on the same local network does not validate coturn — it succeeds peer-to-peer without ever using the relay.

Advanced configuration: recording, authentication and limits

Meeting recording. Jitsi includes Jibri for local recording or RTMP streaming. Jibri requires a second VPS or at minimum 4 additional vCPU, as it captures the browser render in real time. Enable ENABLE_RECORDING=1 in .env and deploy a separate Jibri container pointing to your Jicofo. Organizer authentication. By default, anyone can create a room. Enable AUTH_TYPE=internal to require an account on the organizer's side while letting guests join freely. Create host accounts with docker exec <prosody-container> prosodyctl --config /config/prosody.cfg.lua register admin meet.yourdomain.com password. Limiting anonymous participants. Add ENABLE_LOBBY=1 so a moderator approves each entry before accessing the room. Combined with AUTH_TYPE=internal, this gives full control over who accesses what.

Hardening: fail2ban on the TURN service

coturn exposes a service reachable from the internet. Install fail2ban and create a filter on failed TURN authentication attempts: coturn logs write ERROR: 401, realm on each failure. A fail2ban jail with maxretry=10 and bantime=600 is enough to deter automated scans without affecting your legitimate users. Verify that fail2ban watches /var/log/coturn.log (default path if coturn runs natively) or mount that file from the container.

Troubleshooting: the four most common errors

Missing audio or video stuck on 'connecting' — coturn + UDP 10000. This is the most frequent failure. Check in this order: (1) ufw status — are TCP 443 and UDP 10000 open? (2) docker compose logs coturn | grep -i error — does coturn start without certificate errors? (3) From an external machine: curl -v telnet://meet.yourdomain.com:3478 — does coturn respond? (4) In browser logs (F12 → Console): do you see ICE failed or failed to gather candidates? If so, coturn is not reachable from outside. Compare TURN_HOST in .env with the actual VPS IP. Connection refused on TCP 443. If Let's Encrypt could not issue a certificate (port 80 was closed on first start), the web container attempts to serve HTTPS with a self-signed certificate that the browser rejects. Fix: open port 80, delete the volume ~/.jitsi-meet-cfg/web and re-run docker compose up -d. Screen sharing refused by the browser. Screen sharing requires a secure context (valid HTTPS). If you see getUserMedia is not supported, the certificate is invalid or the domain is not on HTTPS. Check PUBLIC_URL — it must start with https:// with a domain whose certificate is valid, not a raw IP. Persistent 'ICE failed' error despite a working coturn. Check the DOCKER_HOST_ADDRESS variable: it must contain the VPS public IP, not the internal Docker container IP (172.17.x.x). If your VPS sits behind cloud NAT (rare but possible), you need the external NAT IP, not the network interface IP.

Monitoring: tracking bandwidth and participants

The JVB video bridge exposes a real-time statistics API at http://localhost:8080/colibri/stats. This JSON endpoint reports the number of active conferences, participants, inbound and outbound bandwidth in bits/s, and packet loss. For continuous monitoring, enable Prometheus support in .env (JVB_ENABLE_STATS=1, PROSODY_ENABLE_METRICS=1) and connect a Prometheus + Grafana stack: an official Jitsi dashboard is available on Grafana Labs (ID 11925) covering CPU, memory, bandwidth and video quality metrics.

Without Prometheus, minimal monitoring is achieved with a cron that queries /colibri/stats and alerts when throughput exceeds a threshold: docker exec jvb curl -s http://localhost:8080/colibri/stats | python3 -c "import json,sys; s=json.load(sys.stdin); print(s.get('bit_rate_download',0), s.get('bit_rate_upload',0))".

To estimate capacity before saturation: one JVB comfortably handles 200 to 250 interactive participants at moderate video quality on a well-connected VPS. Beyond that, the packet_loss_fraction and rtt_aggregate metrics climb and perceived quality drops before the CPU saturates — that is the signal to add another JVB instance.

Your Jitsi is running — manage bandwidth

Jitsi runs in SFU mode: the video bridge (JVB) relays each stream to each participant, so outbound bandwidth scales with the number of participants multiplied by the number of streams. For 10 participants at 720p, expect 20 to 40 Mbps outbound continuously. Enable lastN=5 (last N active videos) to cap client-side streams in large meetings, and watch docker stats jvb to anticipate saturation. Above about twenty regular simultaneous participants, deploy several JVB instances on separate VPSes behind a single Jicofo: that is Jitsi's native horizontal scaling architecture. For multi-organization or multi-domain deployments, Jitsi supports multiple PUBLIC_URL entries via the Prosody tenant mechanism: each organization gets its own isolated room namespace on the same installation, with no additional infrastructure.

Launch your Jitsi server on a ServOrbit Cloud VPS

A Cloud VPS with a dedicated public IP, generous bandwidth and root access lets you deploy Jitsi Meet, coturn and Jibri with no meeting limit. Your video calls stay on your infrastructure.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab