# LiveKit SFU. Верифицировано по официальной документации # (github.com/livekit/livekit config-sample.yaml + configuration.md). # Реальный API key/secret — из .env (LIVEKIT_API_KEY / LIVEKIT_API_SECRET); # этот файл намеренно не содержит `keys:` — ключи передаются через # LIVEKIT_KEYS (см. docker-compose.yml, формат "key: secret"). # # LiveKit НЕ читает переменные окружения внутри своего YAML-конфига (в # отличие от `keys:`, которые подаются отдельно через LIVEKIT_KEYS) — # статические поля (use_external_ip/node_ip/webhook.api_key) рендерятся # из этого шаблона в livekit.yaml скриптом deploy/render-templates.sh # (envsubst) ПЕРЕД `docker compose up`. Не редактируйте livekit.yaml # напрямую — правки затрутся при следующем рендере. port: 7880 rtc: tcp_port: 7881 # Диапазон сужен для dev (см. комментарий в docker-compose.yml); в проде # расширить и синхронизировать с пробросом портов. port_range_start: 54000 port_range_end: 54100 # use_external_ip: false + node_ip=127.0.0.1 — режим для локальной # разработки (Docker Desktop): use_external_ip=true определяет публичный # IP через STUN, что в контейнере на macOS даёт недостижимый изнутри хоста # внутренний IP (172.18.x.x) — DTLS-хендшейк по data-каналам не проходит # ("dtls timeout" в логах). node_ip=127.0.0.1 работает, потому что порты # 7881/tcp и 54000-54100/udp проброшены на loopback хоста, а браузер-клиент # запускается на том же хосте. # В проде (LIVEKIT_USE_EXTERNAL_IP=true, LIVEKIT_NODE_IP=<внешний IP/домен # сервера> в .env) клиенты снаружи хоста подключаются по этому адресу — # сервер не должен быть за NAT без проброса портов 1:1. use_external_ip: ${LIVEKIT_USE_EXTERNAL_IP} node_ip: ${LIVEKIT_NODE_IP} # Внешний TURN (сервис coturn, профиль `media`) — АНОНС КЛИЕНТАМ. # Сам SFU через эти серверы не ходит: LiveKit лишь отдаёт их браузеру в # списке ICE-серверов при подключении (см. iceServersForParticipant в # pkg/service/roommanager.go), а клиент уже решает, нужен ли ему relay. # # Зачем. До 28.07.2026 coturn работал, но КЛИЕНТЫ О НЁМ НЕ ЗНАЛИ: секция # `turn` ниже выключена (встроенный TURN не поднимаем), внешний в конфиге # объявлен не был, а фронтенд `iceServers` не задаёт. За всё время работы # в логах coturn — ноль ALLOCATE. Итог: у клиентов из сетей с жёстким NAT # не было relay-фолбэка вообще, только прямой UDP и TCP 7881. Именно так # объясняются `PEER_CONNECTION_DISCONNECTED` на нагрузочном тесте — все # у внешних участников, ни одного у офисных (.forcc/LOAD-FINDINGS.md, # причина C). # # `secret` обязан совпадать с `static-auth-secret` в turnserver.conf — # оба рендерятся из одного TURN_STATIC_AUTH_SECRET (deploy/render-templates.sh). # Логин/пароль LiveKit генерирует сам по механизму TURN REST API. # # UDP и TCP на 3478 — оба порта уже открыты в ufw. TLS (5349) намеренно не # объявляем: в turnserver.conf сертификаты не смонтированы, и анонс # неработающего `turns:` заставил бы клиента впустую ждать таймаута, # прежде чем перейти к рабочему кандидату. turn_servers: - host: ${TURN_EXTERNAL_IP} port: 3478 protocol: udp secret: ${TURN_STATIC_AUTH_SECRET} ttl: 14400 - host: ${TURN_EXTERNAL_IP} port: 3478 protocol: tcp secret: ${TURN_STATIC_AUTH_SECRET} ttl: 14400 # Redis обязателен для сервиса egress (см. deploy/egress/) — он использует # его как pub/sub и key-value хранилище состояния запущенных записей; # без него egress не может получать room/track-события от LiveKit # (проверено по официальной документации livekit/egress, раздел "Running # locally"). LiveKit сам по себе тоже использует redis для координации # между узлами кластера (здесь один узел, но сервис оставлен включённым). # password подставляется рендером (deploy/render-templates.sh) из # REDIS_PASSWORD в .env — redis в docker-compose.yml запускается с # --requirepass, без пароля LiveKit не подключится (см. # .forcc/deploy/SESSION2-FINDINGS.md про redis без пароля). redis: address: redis:6379 password: ${REDIS_PASSWORD} # TURN обслуживает отдельный сервис coturn (профиль `media`, deploy/coturn). # При публичной экспозиции LiveKit разместите coturn/TURN-TLS на 443 и # держите эту секцию отключённой, чтобы не поднимать два TURN-сервера. turn: enabled: false # Webhook-приёмник backend'а: события room_started/participant_joined/ # participant_left/room_finished подписываются ключом api_key, который # должен совпадать с одним из ключей в LIVEKIT_KEYS (см. выше и # docker-compose.yml) — берём тот же LIVEKIT_API_KEY из .env. webhook: api_key: ${LIVEKIT_API_KEY} urls: - http://backend:8000/api/v1/livekit/webhook # Если backend запускается на хосте (uvicorn вне docker-compose, а # LiveKit — внутри), используйте вместо этого: # - http://host.docker.internal:8000/api/v1/livekit/webhook logging: level: info json: false # Prometheus metrics (scraped by the monitoring profile). prometheus: port: 6789