Jitsi Meet в Docker Compose: готовый файл
Zoom режут лимитом в 40 минут, у Google Meet и Microsoft Teams нет аккаунта без привязки к рабочему домену, а корпоративная политика иногда прямо запрещает пускать видеозвонки через зарубежные SaaS. Jitsi Meet решает это без компромиссов: участники заходят по ссылке в браузере, ничего не устанавливают, а вся инфраструктура — ваш сервер. Ниже — рабочий docker-compose.yml, который поднимает Jitsi за 15-20 минут, и разбор мест, где обычно спотыкаются при первом запуске.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Jitsi Meet и зачем поднимать его самому
Jitsi Meet — open-source платформа для видеоконференций: браузер участника подключается к WebRTC-медиасерверу, который смешивает и раздаёт потоки без плагинов и приложений. Публичный meet.jit.si работает бесплатно, но для рабочих созвонов он не годится — очередь на серверы у Jitsi непредсказуема, а конфиденциальные данные (что обсуждают, кто участвовал) уходят на чужую инфраструктуру.
Self-hosted вариант даёт три вещи, которых нет в SaaS:
- Контроль над данными — записи, чаты и метаданные звонков не покидают ваш сервер;
- Без лимитов по времени и числу участников — упирается только в ресурсы сервера;
- Свой домен и брендинг — ссылка вида
meet.вашкомпания.ruвместо чужого поддомена.
Минус один, но существенный: медиасервер Jitsi (JVB, Jitsi Videobridge) требует статичный внешний IP и приличную полосу — на каждого участника видеозвонка уходит от 500 Кбит/с до нескольких Мбит/с в обе стороны в зависимости от разрешения. Для 10-15 одновременных участников с видео нужен сервер уровня 4 vCPU / 8 GB RAM и стабильный аплинк от 100 Мбит/с.
Архитектура: из чего состоит стек
Jitsi Meet — это не один контейнер, а связка из пяти сервисов, которые вместе образуют полноценный медиасервер:
| Компонент | Роль |
|---|---|
web | Nginx + фронтенд-интерфейс, точка входа для браузера |
prosody | XMPP-сервер — сигнальный канал, через него участники договариваются о подключении |
jicofo | Jitsi Conference Focus — управляет комнатами и медиапотоками |
jvb | Jitsi Videobridge — принимает и раздаёт видео/аудио между участниками (SFU) |
jigasi (опционально) | Мост для входа по обычному SIP/телефону |
Все сервисы, кроме jvb, общаются между собой во внутренней Docker-сети и наружу не смотрят. jvb — единственный компонент, которому нужен прямой доступ в интернет: он открывает UDP-порт для медиапотоков и должен знать реальный внешний IP сервера, иначе участники за NAT не смогут подключиться.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПодготовка сервера
Понадобится Ubuntu 24.04 (или Debian 12), Docker и Docker Compose plugin, а также домен, указывающий A-записью на IP сервера. Без домена Jitsi можно запустить только для локального теста — HTTPS и WebRTC в браузере требуют либо валидного сертификата, либо localhost.
apt update && apt install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Если ставите с нуля, порядок действий обычно такой: сначала настроить домен и DNS, потом поднять UFW и уже после этого — Docker-стек. Проверьте, что порты сервера не заняты другим веб-сервером на 80/443 — Jitsi в этой конфигурации сам займёт их через встроенный web-контейнер.
docker-compose.yml для Jitsi Meet
Официальный образ Jitsi публикует переменные окружения через .env-файл, а не через ручную правку конфигов внутри контейнеров — это упрощает апдейты. Создаём структуру:
mkdir -p /opt/jitsi && cd /opt/jitsi
mkdir -p ./jitsi-meet-cfg/{web,transcripts,prosody/config,prosody/prosody-plugins,jicofo,jvb,jigasi,jibri}
Файл .env (генерируем секреты и заполняем обязательные поля):
cat <<'EOF' > .env
CONFIG=./jitsi-meet-cfg
HTTP_PORT=8000
HTTPS_PORT=8443
TZ=Europe/Moscow
PUBLIC_URL=https://meet.example.com
DOCKER_HOST_ADDRESS=YOUR.SERVER.PUBLIC.IP
JICOFO_AUTH_PASSWORD=$(openssl rand -hex 16)
JVB_AUTH_PASSWORD=$(openssl rand -hex 16)
JIGASI_XMPP_PASSWORD=$(openssl rand -hex 16)
JIBRI_RECORDER_PASSWORD=$(openssl rand -hex 16)
JIBRI_XMPP_PASSWORD=$(openssl rand -hex 16)
ENABLE_LETSENCRYPT=1
LETSENCRYPT_DOMAIN=meet.example.com
LETSENCRYPT_EMAIL=you@example.com
JVB_ADVERTISE_IPS=YOUR.SERVER.PUBLIC.IP
EOF
$(openssl rand -hex 16) в heredoc не подставится сам — либо сгенерируйте значения командой заранее и вставьте вручную, либо соберите .env через отдельный скрипт с реальной подстановкой переменных. Замените meet.example.com, you@example.com и YOUR.SERVER.PUBLIC.IP на реальные значения.
Сам docker-compose.yml:
services:
web:
image: jitsi/web:stable
restart: unless-stopped
ports:
- '${HTTP_PORT:-80}:80'
- '${HTTPS_PORT:-443}:443'
volumes:
- ${CONFIG}/web:/config
- ${CONFIG}/web/letsencrypt:/etc/letsencrypt
- ${CONFIG}/transcripts:/usr/share/jitsi-meet/transcripts
environment:
- ENABLE_LETSENCRYPT
- LETSENCRYPT_DOMAIN
- LETSENCRYPT_EMAIL
- PUBLIC_URL
- JICOFO_AUTH_USER=focus
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_BOSH_URL_BASE=http://xmpp.meet.jitsi:5280
- XMPP_GUEST_DOMAIN=guest.meet.jitsi
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- XMPP_SERVER=xmpp.meet.jitsi
networks:
meet.jitsi:
aliases:
- ${XMPP_DOMAIN:-meet.jitsi}
prosody:
image: jitsi/prosody:stable
restart: unless-stopped
expose:
- '5222'
- '5347'
- '5280'
volumes:
- ${CONFIG}/prosody/config:/config
- ${CONFIG}/prosody/prosody-plugins:/prosody-plugins-custom
environment:
- AUTH_TYPE=internal
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_GUEST_DOMAIN=guest.meet.jitsi
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- JICOFO_COMPONENT_SECRET
- JICOFO_AUTH_USER=focus
- JICOFO_AUTH_PASSWORD
- JVB_AUTH_USER=jvb
- JVB_AUTH_PASSWORD
- JIGASI_XMPP_USER=jigasi
- JIGASI_XMPP_PASSWORD
- ENABLE_GUESTS=1
networks:
meet.jitsi:
aliases:
- xmpp.meet.jitsi
jicofo:
image: jitsi/jicofo:stable
restart: unless-stopped
volumes:
- ${CONFIG}/jicofo:/config
environment:
- XMPP_DOMAIN=meet.jitsi
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- XMPP_SERVER=xmpp.meet.jitsi
- JICOFO_COMPONENT_SECRET
- JICOFO_AUTH_USER=focus
- JICOFO_AUTH_PASSWORD
depends_on:
- prosody
networks:
- meet.jitsi
jvb:
image: jitsi/jvb:stable
restart: unless-stopped
ports:
- '10000:10000/udp'
- '4443:4443/tcp'
volumes:
- ${CONFIG}/jvb:/config
environment:
- DOCKER_HOST_ADDRESS
- XMPP_AUTH_DOMAIN=auth.meet.jitsi
- XMPP_MUC_DOMAIN=muc.meet.jitsi
- XMPP_SERVER=xmpp.meet.jitsi
- JVB_AUTH_USER=jvb
- JVB_AUTH_PASSWORD
- JVB_ADVERTISE_IPS
- JVB_STUN_SERVERS=meet-jit-si-turnrelay.jitsi.net:443
- JICOFO_AUTH_USER=focus
depends_on:
- prosody
networks:
- meet.jitsi
networks:
meet.jitsi:
Обратите внимание на 10000:10000/udp — это ключевой порт медиапотоков. Если сервер за NAT или облачным файрволом, проброс UDP-порта надо продублировать в панели провайдера отдельно от ufw.
Запуск и первая проверка
cd /opt/jitsi
docker compose up -d
docker compose ps
docker compose logs -f jvb
Первый запуск Let's Encrypt внутри контейнера web может занять минуту-две — сертификат заказывается автоматически по LETSENCRYPT_DOMAIN. Если через пять минут https://meet.example.com всё ещё отдаёт self-signed сертификат браузера, проверьте:
docker compose logs web | grep -i certbot
Частая причина отказа — 80/443 порт занят другим сервисом (например, уже стоящим Nginx или Caddy), и ACME-challenge Let's Encrypt не проходит валидацию домена. Если на сервере уже есть Caddy с авто-SSL или отдельный reverse-proxy, логичнее отключить ENABLE_LETSENCRYPT в Jitsi и выпустить сертификат через существующий прокси, а web-контейнер Jitsi посадить на внутренний порт.
Создайте тестовую комнату, откройте её в двух вкладках (или с телефона) и проверьте видео и звук. Если картинка есть, а звонок обрывается через 10-15 секунд — почти всегда это NAT-проблема с JVB_ADVERTISE_IPS: значение должно быть реальным публичным IP сервера, а не внутренним адресом контейнера.
Настройка под нагрузку: сколько ресурсов реально нужно
Ориентировочно (точные цифры зависят от разрешения видео, кодека и числа активных камер):
- До 5 участников, только звук + иногда видео — 2 vCPU / 4 GB RAM хватает с запасом;
- 10-15 участников с видео — 4 vCPU / 8 GB RAM, полоса от 100 Мбит/с;
- 20+ участников или регулярные вебинары — 8 vCPU / 16 GB RAM и отдельный вынесенный
jvbна своём сервере с прямым публичным IP, чтобы разгрузить основной хост.
Jicofo и Prosody лёгкие, всю нагрузку тянет jvb — он транскодирует и ретранслирует медиапотоки. Для десятков одновременных конференций JVB масштабируется горизонтально: несколько экземпляров подключаются к одному prosody/jicofo, и нагрузка распределяется автоматически — это отдельная настройка сверх базового docker-compose.yml, но архитектура Jitsi это поддерживает из коробки.
Для контроля потребления полезно сразу выставить лимиты CPU/памяти в compose-файле (см. лимиты ресурсов в Docker), а для долгоиграющей инфраструктуры — базовый мониторинг сервера, чтобы вовремя заметить нехватку диска под логи.
Обслуживание: обновления, бэкапы, запись звонков
Обновление стека — замена тега образа и пересоздание контейнеров:
cd /opt/jitsi
docker compose pull
docker compose up -d
Перед обновлением стоит свериться с release notes Jitsi — формат .env-переменных иногда меняется между релизами, и старый файл может не подойти к новому образу без правок.
Бэкапить нужно директорию ./jitsi-meet-cfg — в ней лежат сертификаты, конфиги Prosody и все пароли компонентов. Сами звонки нигде не хранятся, если не подключён отдельный сервис записи (Jibri) — по умолчанию Jitsi Meet ничего не пишет на диск, конференция существует только пока идёт.
tar czf jitsi-backup-$(date +%F).tar.gz /opt/jitsi/.env /opt/jitsi/jitsi-meet-cfg /opt/jitsi/docker-compose.yml
Если нужна запись встреч — это отдельный компонент Jibri, который управляет headless Chromium внутри своего контейнера и требует заметно больше ресурсов (ещё 2-4 GB RAM на активную запись). Разворачивать его имеет смысл только когда запись — обязательное требование, а не "на всякий случай".
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Участники видят чёрный экран или обрыв связи при видео — в чём дело?
Почти всегда — проблема с UDP-портом 10000 или неверным JVB_ADVERTISE_IPS. Проверьте, что порт проброшен на уровне облачного файрвола провайдера (не только ufw на сервере), и что в .env указан именно публичный, а не внутренний IP.
Можно ли обойтись без своего домена?
Технически да, через self-signed сертификат, но большинство браузеров заблокирует доступ к камере/микрофону на "небезопасном" сайте без валидного HTTPS. Для рабочего использования домен обязателен.
Сколько человек реально выдержит один звонок?
Зависит от сервера и разрешения видео, но 15-20 участников с включёнными камерами на 4 vCPU / 8 GB — реалистичный ориентир. Больше — нужен вынесенный JVB или снижение разрешения через настройки клиента.
Чем Jitsi отличается от Mattermost Calls или BigBlueButton?
Jitsi — специализированный видеосервис без чата и задач, его удобно встраивать как отдельный сервис "звонок по ссылке". Если нужна полноценная корпоративная коммуникация с каналами и историей сообщений, посмотрите в сторону Mattermost — у него есть встроенные звонки, хотя и менее гибкие по масштабированию, чем отдельный Jitsi.
Нужен ли TURN-сервер отдельно?
Для большинства случаев хватает STUN-сервера Jitsi по умолчанию (meet-jit-si-turnrelay.jitsi.net). Свой TURN нужен, если у значительной части участников строгий Symmetric NAT — тогда прямое медиасоединение не устанавливается и требуется релей.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →