- Домен: В примере используется
example.com.matrix.example.com— основной сервер Synapse.matrix.example.com/admin— админка Synapse.chat.example.com— веб-клиент Element.rtc.example.com— сервер LiveKit (сигналинг и медиа).
- VPS/Server: Linux, установленный Docker и Docker Compose.
- Порты:
- TCP: 80, 443 (для Traefik)
- TCP: 7881 (LiveKit RTC)
- UDP: 50100-50200 (диапазон для медиа-потоков LiveKit, нужно открыть в Firewall)
Создайте следующую структуру проекта:
matrix-server/
├── docker-compose.yml
├── traefik/
│ ├── traefik.yml
│ └── dynamic.yml
├── matrix/
│ ├── synapse/ # Монтируется как /data в контейнер synapse
│ ├── well-known/
│ │ ├── matrix/
│ │ │ ├── client
│ │ │ └── server
│ ├── livekit.yaml
│ ├── web-config.json # Конфиг для Element
│ └── synapse-admin-config.json
Вам понадобятся случайные строки для секретов. Сгенерируйте их (например, через openssl rand -hex 32) и сохраните где-нибудь. Нам понадобятся:
REGISTRATION_SHARED_SECRET(для регистрации)MACAROON_SECRET(для Synapse)LIVEKIT_API_KEY(назовемdevkeyили как удобно)LIVEKIT_API_SECRET(длинный хеш)
Файл traefik/traefik.yml (статическая конфигурация):
entryPoints:
http:
address: ":80"
https:
address: ":443"
http:
routers:
http-catchall:
rule: hostregexp(`{host:.+}`)
entrypoints:
- http
middlewares:
- redirect-to-https
middlewares:
redirect-to-https:
redirectScheme:
scheme: https
permanent: false
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
watch: true
exposedByDefault: false
file:
filename: /traefik/dynamic.yml
watch: true
certificatesResolvers:
letsEncrypt:
acme:
email: your-email@example.com # <-- УКАЖИТЕ СВОЮ ПОЧТУ
storage: /traefik/acme.json
caServer: "https://acme-v02.api.letsencrypt.org/directory"
httpChallenge:
entryPoint: httpФайл traefik/dynamic.yml можно оставить пустым или добавить туда TLS настройки, но файл должен существовать.
Файл matrix/livekit.yaml. Здесь важно указать ключи, совпадающие с теми, что будут в Synapse и JWT сервисе.
port: 7880
bind_addresses:
- '0.0.0.0'
rtc:
tcp_port: 7881
port_range_start: 50100
port_range_end: 50200
use_external_ip: true
room:
auto_create: false
logging:
level: info
turn:
enabled: false
keys:
# Пример пары ключ:секрет. Замените на свои!
devkey: 'ВАШ_СГЕНЕРИРОВАННЫЙ_LIVEKIT_SECRET'Это критически важная часть. Именно через эти файлы клиенты узнают, где находится сервер LiveKit.
Файл matrix/well-known/matrix/client:
{
"m.homeserver": { "base_url": "https://matrix.example.com" },
"org.matrix.msc4143.rtc_foci": [
{
"type": "livekit",
"livekit_service_url": "https://rtc.example.com/livekit/jwt"
}
]
}Файл matrix/well-known/matrix/server:
{
"m.server": "matrix.example.com:443"
}Файл matrix/web-config.json (для Element Web):
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.example.com",
"server_name": "matrix.example.com"
}
},
"disable_identity_server": true,
"brand": "My Matrix",
"roomDirectory": { "servers": ["matrix.example.com"] },
"showLabsSettings": true
}Файл matrix/synapse-admin-config.json:
{
"restrictBaseUrl": "https://matrix.example.com"
}Основной файл docker-compose.yml. Обратите внимание на переменные environment для сервиса lk-jwt — ключи должны совпадать.
services:
traefik:
image: traefik:v2.11
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- 80:80
- 443:443
volumes:
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik:/traefik
- ./traefik/traefik.yml:/traefik.yml:ro
- ./traefik/dynamic.yml:/traefik/dynamic.yml:ro
synapse:
image: matrixdotorg/synapse:latest
container_name: synapse
restart: unless-stopped
environment:
- SYNAPSE_SERVER_NAME=matrix.example.com
- SYNAPSE_REPORT_STATS=no
volumes:
- ./matrix/synapse:/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.synapse.rule=Host(`matrix.example.com`) && PathPrefix(`/_matrix`)"
- "traefik.http.routers.synapse.entrypoints=https"
- "traefik.http.routers.synapse.tls.certresolver=letsEncrypt"
- "traefik.http.routers.synapse.service=synapse"
- "traefik.http.services.synapse.loadbalancer.server.port=8008"
# API Admin (необязательно выставлять наружу, если есть VPN, но вот пример)
- "traefik.http.routers.synapse_admin_api.rule=Host(`matrix.example.com`) && PathPrefix(`/_synapse`)"
- "traefik.http.routers.synapse_admin_api.entrypoints=https"
- "traefik.http.routers.synapse_admin_api.tls.certresolver=letsEncrypt"
- "traefik.http.routers.synapse_admin_api.service=synapse"
livekit:
image: livekit/livekit-server:latest
container_name: livekit
command: --config /etc/livekit.yaml
restart: unless-stopped
volumes:
- ./matrix/livekit.yaml:/etc/livekit.yaml:ro
ports:
- "7881:7881/tcp"
- "50100-50200:50100-50200/udp"
labels:
- "traefik.enable=true"
# Роутинг SFU
- "traefik.http.routers.livekit_sfu.rule=Host(`rtc.example.com`) && PathPrefix(`/livekit/sfu`)"
- "traefik.http.routers.livekit_sfu.entrypoints=https"
- "traefik.http.routers.livekit_sfu.tls.certresolver=letsEncrypt"
- "traefik.http.routers.livekit_sfu.middlewares=lk_sfu_strip"
- "traefik.http.routers.livekit_sfu.service=livekit_sfu"
- "traefik.http.services.livekit_sfu.loadbalancer.server.port=7880"
- "traefik.http.middlewares.lk_sfu_strip.stripprefix.prefixes=/livekit/sfu,/livekit/sfu/"
lk-jwt:
image: ghcr.io/element-hq/lk-jwt-service:latest
container_name: livekit-jwt
restart: unless-stopped
environment:
- PORT=8080
- LIVEKIT_JWT_PORT=8080
- LIVEKIT_URL=wss://rtc.example.com/livekit/sfu
- LIVEKIT_KEY=devkey
- LIVEKIT_SECRET=ВАШ_СГЕНЕРИРОВАННЫЙ_LIVEKIT_SECRET
- LIVEKIT_FULL_ACCESS_HOMESERVERS=matrix.example.com
labels:
- "traefik.enable=true"
- "traefik.http.routers.lk_jwt.rule=Host(`rtc.example.com`) && PathPrefix(`/livekit/jwt`)"
- "traefik.http.routers.lk_jwt.entrypoints=https"
- "traefik.http.routers.lk_jwt.tls.certresolver=letsEncrypt"
- "traefik.http.routers.lk_jwt.middlewares=lk_jwt_strip,lk_cors"
- "traefik.http.routers.lk_jwt.service=lk_jwt"
- "traefik.http.services.lk_jwt.loadbalancer.server.port=8080"
# CORS Preflight
- "traefik.http.routers.lk_jwt_preflight.rule=Host(`rtc.example.com`) && PathPrefix(`/livekit/jwt`) && Method(`OPTIONS`)"
- "traefik.http.routers.lk_jwt_preflight.entrypoints=https"
- "traefik.http.routers.lk_jwt_preflight.tls.certresolver=letsEncrypt"
- "traefik.http.routers.lk_jwt_preflight.middlewares=lk_cors"
- "traefik.http.routers.lk_jwt_preflight.service=noop@internal"
# Middlewares
- "traefik.http.middlewares.lk_cors.headers.accesscontrolalloworigin=*"
- "traefik.http.middlewares.lk_cors.headers.accesscontrolallowmethods=GET,POST,OPTIONS"
- "traefik.http.middlewares.lk_cors.headers.accesscontrolallowheaders=Content-Type,Authorization"
- "traefik.http.middlewares.lk_jwt_strip.stripprefix.prefixes=/livekit/jwt,/livekit/jwt/"
wellknown:
image: nginx:alpine
container_name: wellknown
restart: unless-stopped
volumes:
- ./matrix/well-known:/usr/share/nginx/html/.well-known:ro
labels:
- "traefik.enable=true"
# Client discovery
- "traefik.http.routers.wk_client.rule=Host(`matrix.example.com`) && PathPrefix(`/.well-known/matrix/client`)"
- "traefik.http.routers.wk_client.priority=1000"
- "traefik.http.routers.wk_client.entrypoints=https"
- "traefik.http.routers.wk_client.tls.certresolver=letsEncrypt"
- "traefik.http.routers.wk_client.middlewares=wk_headers"
- "traefik.http.routers.wk_client.service=wk"
# Server discovery
- "traefik.http.routers.wk_server.rule=Host(`matrix.example.com`) && PathPrefix(`/.well-known/matrix/server`)"
- "traefik.http.routers.wk_server.priority=1000"
- "traefik.http.routers.wk_server.entrypoints=https"
- "traefik.http.routers.wk_server.tls.certresolver=letsEncrypt"
- "traefik.http.routers.wk_server.middlewares=wk_headers"
- "traefik.http.routers.wk_server.service=wk"
- "traefik.http.middlewares.wk_headers.headers.customResponseHeaders.Access-Control-Allow-Origin=*"
- "traefik.http.middlewares.wk_headers.headers.customResponseHeaders.Content-Type=application/json"
- "traefik.http.services.wk.loadbalancer.server.port=80"
element-web:
image: vectorim/element-web:latest
container_name: element-web
restart: unless-stopped
volumes:
- ./matrix/web-config.json:/app/config.json:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.element.rule=Host(`chat.example.com`)"
- "traefik.http.routers.element.entrypoints=https"
- "traefik.http.routers.element.tls.certresolver=letsEncrypt"
- "traefik.http.services.element.loadbalancer.server.port=80"
synapse-admin:
image: awesometechnologies/synapse-admin:latest
container_name: synapse-admin
restart: unless-stopped
environment:
- REACT_APP_SERVER=https://matrix.example.com
volumes:
- ./matrix/synapse-admin-config.json:/app/config.json:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.synapse_admin_ui.rule=Host(`matrix.example.com`) && PathPrefix(`/admin`)"
- "traefik.http.routers.synapse_admin_ui.entrypoints=https"
- "traefik.http.routers.synapse_admin_ui.tls.certresolver=letsEncrypt"
- "traefik.http.middlewares.admin_redirect.redirectregex.regex=^(.*)/admin/?$"
- "traefik.http.middlewares.admin_redirect.redirectregex.replacement=$${1}/admin/"
- "traefik.http.middlewares.admin_strip.stripprefix.prefixes=/admin"
- "traefik.http.routers.synapse_admin_ui.middlewares=admin_redirect,admin_strip"
- "traefik.http.services.synapse_admin_ui.loadbalancer.server.port=80"-
Запустите контейнер Synapse один раз, чтобы сгенерировать конфиг (если его еще нет):
docker run -it --rm \ -v "$PWD/matrix/synapse:/data" \ -e SYNAPSE_SERVER_NAME=matrix.example.com \ -e SYNAPSE_REPORT_STATS=no \ matrixdotorg/synapse:latest generate -
Отредактируйте созданный файл
matrix/synapse/homeserver.yaml. В него нужно добавить поддержку экспериментальных функций для LiveKit/WebRTC.
Внесите следующие изменения в homeserver.yaml:
# Основные настройки
server_name: "matrix.example.com"
public_baseurl: "https://matrix.example.com/"
# Включаем экспериментальные фичи (обязательно для нативного WebRTC)
experimental_features:
msc3266_enabled: true
msc4222_enabled: true
msc4140_enabled: true # Native Element Call
# Если используете SQLite (для теста), или настройте Postgres
database:
name: sqlite3
args:
database: /data/homeserver.db
# Настройка интеграции с LiveKit (ключи как в livekit.yaml и docker-compose)
livekit_jwt_sso:
enabled: true
# URL к сервису lk-jwt (через внешний адрес или внутреннюю сеть)
livekit_jwt_sso_url: "https://rtc.example.com/livekit/jwt/token"
livekit_secret: "ВАШ_СГЕНЕРИРОВАННЫЙ_LIVEKIT_SECRET"
# Секреты (замените на свои сгенерированные)
registration_shared_secret: "ВАШ_SECRET_1"
macaroon_secret_key: "ВАШ_SECRET_2"Запускаем всё хозяйство:
docker-compose up -dПроверяем логи, особенно livekit и synapse:
docker-compose logs -f livekit
- Пользователь заходит на
chat.example.com. - Element запрашивает
https://matrix.example.com/.well-known/matrix/client. - Сервер отвечает JSON-ом, в котором сказано: "Для звонков используй
rtc.example.com(LiveKit)". - При начале звонка Synapse генерирует токена через ваш
lk-jwtсервис. - Element устанавливает соединение с LiveKit по UDP (порты 50100-50200) для медиа и WebSocket для сигналинга.
Для регистрации нужно создавать пользователям аккаунты на matrix.example.com/admin
Всем привет! где взять под эту статью настройки nginx?