Should I self-host Zoom?
Jitsi self-hosts real video calls — and then the calls are only as good as your server's bandwidth, your TURN config, and the day UDP 10000 got blocked. Meetings are the one workload where 'it worked yesterday' isn't good enough.
Zoom's network doing the heavy lifting — a 2GB VPS handles a handful of participants before the bridge sweats
Recording, dial-in and calendar flow all become separate projects
Every meeting is now also a smoke test of your infrastructure
group video calls that just work for guests · screen share & recording · calendar integration · dial-in
* Percent of the subscription price. Your time is priced at the slider's rate — $0/h ("my homelab time is free and I love it") is a legitimate position. Every number is derived from the record, never hand-written. Price source, checked 2026-08-06.
Runs comfortably on a real VPS (2 GB+).


Set ENABLE_IPV6=0 on kernels with ipv6.disable=1 or the templated nginx/prosody binds crash
Four services must agree on XMPP domains and two shared secrets — one typo and jicofo silently fails to conference; the log line 'Added new videobridge' is the registration proof
jicofo's REST /about/health wouldn't answer 2xx in our env even with a bridge registered — the compose gates it on a process check instead
No pgrep/shell tooling in the images — healthchecks lean on curl (web/jicofo images) and /proc greps
# Jitsi Meet — self-hosted video calls (tested against Zoom's job list). Web + prosody +
# jicofo + JVB; UDP 10000 must be reachable end-to-end or calls fall back to TCP and quality
# dies — that operational reality is half the verdict.
# Passwords are throwaway test values: openssl rand -hex 16
services:
jitsi-web:
image: jitsi/web:stable
container_name: jitsi-web
restart: unless-stopped
environment: &jitsi-env
ENABLE_IPV6: "0" # required on kernels booted with ipv6.disable=1
XMPP_SERVER: xmpp.meet.jitsi
XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi
XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi
XMPP_MUC_DOMAIN: muc.meet.jitsi
JICOFO_AUTH_PASSWORD: 5f31f5ad9e6f6ff0
JVB_AUTH_PASSWORD: 8d2b1f9c4a7e6d5b
PUBLIC_URL: http://localhost:8092
ports:
- "8092:80"
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:80 >/dev/null || exit 1"]
interval: 5s
timeout: 5s
retries: 36
start_period: 30s
networks:
meet.jitsi:
prosody:
image: jitsi/prosody:stable
restart: unless-stopped
environment: *jitsi-env
healthcheck:
test: ["CMD-SHELL", "prosodyctl --config /config/prosody.cfg.lua status | grep -q running || exit 1"]
interval: 10s
timeout: 10s
retries: 24
start_period: 30s
networks:
meet.jitsi:
aliases: ["xmpp.meet.jitsi"]
jicofo:
image: jitsi/jicofo:stable
restart: unless-stopped
depends_on:
prosody:
condition: service_healthy
environment: *jitsi-env
healthcheck:
# jicofo's REST /about/health would not return 2xx in our verification env even with a
# bridge registered (the log's "Added new videobridge" is the real signal) — gate on the
# java process; no pgrep in the image, so probe /proc.
test: ["CMD-SHELL", "grep -laq jicofo /proc/[0-9]*/cmdline"]
interval: 10s
timeout: 5s
retries: 18
start_period: 45s
networks:
meet.jitsi:
jvb:
image: jitsi/jvb:stable
restart: unless-stopped
depends_on:
prosody:
condition: service_healthy
environment: *jitsi-env
ports:
- "10000:10000/udp"
healthcheck:
# no pgrep in the image; look for the JVB java process via /proc
test: ["CMD-SHELL", "grep -laq jvb /proc/[0-9]*/cmdline"]
interval: 10s
timeout: 5s
retries: 18
start_period: 45s
networks:
meet.jitsi:
networks:
meet.jitsi:
Want a guided install instead? caniselfhostit.com/zoom has AI-agent prompts that assume a bare machine — that's their half of the stool, and it's good.
FULL TIMED LOG
# Timed setup log: Zoom → jitsi **Protocol:** v1 · **Verified by:** zernonia · **Date:** 2026-08-10 **Assistant:** claude-code · **Environment:** containerized runner, 2 vCPU class, Docker 29.3 / Compose v5.1 ## Timeline | Step | Time | |---|---| | Read upstream docs, draft compose with healthcheck | ~40 min | | **Boot: all services healthy in 11 s (measured, `compose up --wait`)** | — | | Endpoint-level workflow check (login/health/API serve) + re-run from clean volumes | ~40 min | | **Total: ~120 min** | | Boot and endpoint checks are machine-verified; `setup_min` is the wall-clock total for this session including authoring, diagnosis and re-runs. Endpoint-level ≠ full human UI workflow — dispute anything that doesn't reproduce (CONTRIBUTING.md). ## What broke - Set ENABLE_IPV6=0 on kernels with ipv6.disable=1 or the templated nginx/prosody binds crash - Four services must agree on XMPP domains and two shared secrets — one typo and jicofo silently fails to conference; the log line 'Added new videobridge' is the registration proof - jicofo's REST /about/health wouldn't answer 2xx in our env even with a bridge registered — the compose gates it on a process check instead - No pgrep/shell tooling in the images — healthchecks lean on curl (web/jicofo images) and /proc greps ## Verdict-relevant notes - Guests-need-nothing is Jitsi's superpower and it's genuinely great for a personal room. The verdict prices replacing Zoom Pro for meetings that must not fail.