Matrix (Synapse) в Docker Compose: готовый файл
Slack и Telegram удобны, но переписка компании лежит на чужой инфраструктуре, а закрыть чат для внешних гостей или подключить его к другому серверу без API-костылей не получится. Matrix решает обе проблемы: это открытый протокол обмена сообщениями со сквозным шифрованием и федерацией — ваш сервер общается с чужими серверами напрямую, как e-mail. Synapse — эталонная реализация домашнего сервера (homeserver) для Matrix, и её действительно можно поднять одним docker-compose.yml. Ниже — рабочий файл, настройка федерации через .well-known и то, на что стоит обратить внимание до того, как на сервер придут первые пользователи.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Matrix и зачем свой Synapse
Matrix — это не мессенджер, а протокол, вокруг которого построены разные клиенты и серверы. Идея та же, что у e-mail: у каждого пользователя есть адрес вида @user:matrix.example.com, где matrix.example.com — ваш homeserver. Сообщения можно отправлять как внутри своего сервера, так и пользователям на чужих серверах — сервера сами обмениваются данными по федеративному протоколу, без единой центральной точки.
Практическая ценность самостоятельного хостинга:
- Данные у вас. История переписки, файлы, метаданные хранятся на вашем сервере, а не у третьей стороны.
- Сквозное шифрование по умолчанию в приватных комнатах (Olm/Megolm) — сервер не видит содержимое сообщений, только маршрутизирует зашифрованные блобы.
- Федерация — по желанию. Можно оставить сервер полностью закрытым (только внутренние пользователи, без обмена с внешним миром) или открыть федерацию и общаться с любым Matrix-сервером, включая крупные публичные комнаты вроде matrix.org.
- Мосты (bridges) позволяют подключить Telegram, WhatsApp, Slack, Discord — Matrix в этом сценарии работает как единый хаб для разных каналов связи, хотя настройка мостов — это уже отдельная тема за рамками сегодняшнего файла.
Из минусов сразу скажу честно: Synapse написан на Python и прожорливее к памяти и диску, чем более лёгкие чаты вроде Mattermost, особенно с включённой федерацией — сервер начинает кэшировать чужие медиафайлы и историю. Если нужен просто внутренний командный чат без федерации, присмотритесь к Mattermost — он проще в обслуживании. Matrix оправдан, когда важны децентрализация, шифрование по умолчанию или связь с внешними Matrix-серверами.
Архитектура стека
Рабочий продакшн-стек состоит из четырёх частей, три из которых — контейнеры:
- synapse — сам homeserver: обрабатывает клиентский API, федерацию, хранит логику комнат;
- postgres — база данных. SQLite, который Synapse тоже поддерживает, годится только для теста на 2-3 пользователей — под реальную нагрузку нужен PostgreSQL;
- coturn — TURN/STUN-сервер для голосовых и видеозвонков через WebRTC. Без него звонки работают только между клиентами в одной сети, за NAT — не пройдут;
- reverse-proxy (nginx или Caddy) снаружи — терминирует TLS и разводит клиентский API и федеративный трафик по нужным путям на 443-м порту.
Важный нюанс: у Matrix есть два адреса. server_name в конфиге — логический домен (example.com), который войдёт в ID пользователей (@user:example.com) и не обязан указывать на IP сервера. Фактический адрес, где физически крутится Synapse, может быть другим (matrix.example.com). Связь между ними задаётся через .well-known/matrix/server и .well-known/matrix/client — файлы на основном домене, которые видят другие Matrix-сервера. Это удобно: server_name остаётся коротким, а сам сервер можно переносить между хостами без смены ID пользователей.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
Структура каталогов на хосте:
matrix/
├── docker-compose.yml
├── .env
├── synapse-data/
│ ├── homeserver.yaml
│ ├── example.com.log.config
│ └── example.com.signing.key
├── postgres-data/
└── coturn/
└── turnserver.conf
Файл .env:
SYNAPSE_SERVER_NAME=example.com
POSTGRES_PASSWORD=замените_на_свой_сложный_пароль
TURN_SECRET=замените_на_случайную_строку
Файл docker-compose.yml:
services:
postgres:
image: postgres:16-alpine
container_name: matrix-postgres
restart: unless-stopped
environment:
POSTGRES_USER: synapse
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: synapse
# Synapse требует именно эту локаль и кодировку для базы
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C"
volumes:
- ./postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U synapse"]
interval: 10s
timeout: 5s
retries: 5
synapse:
image: matrixdotorg/synapse:latest
container_name: matrix-synapse
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
SYNAPSE_SERVER_NAME: ${SYNAPSE_SERVER_NAME}
SYNAPSE_REPORT_STATS: "no"
volumes:
- ./synapse-data:/data
ports:
- "8008:8008" # клиентский API, наружу отдаст reverse-proxy
coturn:
image: coturn/coturn:latest
container_name: matrix-coturn
restart: unless-stopped
network_mode: host
volumes:
- ./coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro
coturn работает в network_mode: host — это осознанный выбор: TURN-серверу нужен доступ ко всему диапазону UDP-портов для медиарелея, пробрасывать их по одному через docker-compose неудобно и создаёт лишнюю нагрузку на iptables.
Синапс-конфиг генерируется отдельной командой перед первым запуском, а не пишется руками с нуля:
mkdir -p matrix/synapse-data matrix/postgres-data matrix/coturn
cd matrix
docker run --rm \
-e SYNAPSE_SERVER_NAME=example.com \
-e SYNAPSE_REPORT_STATS=no \
-v "$(pwd)/synapse-data:/data" \
matrixdotorg/synapse:latest generate
Команда создаст homeserver.yaml, ключ подписи и лог-конфиг с настройками по умолчанию (SQLite). Дальше в homeserver.yaml правим блок базы данных на PostgreSQL:
database:
name: psycopg2
args:
user: synapse
password: замените_на_свой_сложный_пароль
database: synapse
host: postgres
port: 5432
cp_min: 5
cp_max: 10
homeserver.yaml и делегирование федерации
Кроме базы данных, в homeserver.yaml стоит явно выставить несколько параметров:
public_baseurl: "https://matrix.example.com/"
enable_registration: false
registration_shared_secret: "замените_на_случайную_строку"
max_upload_size: 100M
turn_uris:
- "turn:example.com:3478?transport=udp"
- "turn:example.com:3478?transport=tcp"
turn_shared_secret: "тот_же_секрет_что_в_turnserver.conf"
turn_user_lifetime: 86400000
enable_registration: false закрывает саморегистрацию — новых пользователей заводит только администратор через registration_shared_secret. Это разумный дефолт для корпоративного сервера; открытая регистрация имеет смысл только для публичного сервера, где вы готовы модерировать поток.
Теперь про .well-known. Если Synapse физически живёт на matrix.example.com, а server_name в конфиге — короткий example.com, на основном домене нужно отдать два JSON-файла. Через nginx это делается прямо в конфиге основного сайта:
server {
listen 443 ssl;
server_name example.com;
location /.well-known/matrix/server {
add_header Content-Type application/json;
return 200 '{"m.server": "matrix.example.com:443"}';
}
location /.well-known/matrix/client {
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.homeserver": {"base_url": "https://matrix.example.com"}}';
}
}
А сам matrix.example.com проксирует трафик в контейнер Synapse — федеративные запросы и клиентский API идут через один и тот же путь на 443-м порту:
server {
listen 443 ssl;
server_name matrix.example.com;
client_max_body_size 100M;
location ~ ^(/_matrix|/_synapse/client) {
proxy_pass http://127.0.0.1:8008;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_read_timeout 300s;
}
}
TLS для обоих доменов удобнее всего получить через Let's Encrypt — если ещё не настраивали, у нас есть отдельная инструкция по выпуску SSL-сертификата, а общий разбор reverse-proxy на nginx — в статье про настройку nginx как обратного прокси. Если предпочитаете Caddy с автоматическим TLS — логика та же, просто без ручной возни с certbot.
Первый пользователь, TURN и выбор клиента
Запускаем стек и создаём администратора:
docker compose up -d
docker compose exec synapse register_new_matrix_user \
-c /data/homeserver.yaml http://localhost:8008
Команда спросит логин, пароль и предложит выдать права администратора — отвечайте yes для первого аккаунта.
Для coturn минимальный рабочий turnserver.conf:
listening-port=3478
tls-listening-port=5349
listening-ip=0.0.0.0
relay-ip=IP_вашего_сервера
external-ip=IP_вашего_сервера
min-port=49160
max-port=49200
use-auth-secret
static-auth-secret=тот_же_секрет_что_в_homeserver.yaml
realm=example.com
cert=/etc/letsencrypt/live/matrix.example.com/fullchain.pem
pkey=/etc/letsencrypt/live/matrix.example.com/privkey.pem
no-tcp-relay
Диапазон min-port–max-port нужно открыть в файрволе по UDP, а 3478 и 5349 — по TCP и UDP. Без TURN звонки будут падать почти у всех пользователей за NAT, так что пропускать этот шаг не стоит, даже если сейчас планируете только текстовый чат.
Клиент для подключения — обычно Element (веб, десктоп, мобильные приложения): при входе сначала указывается адрес homeserver (https://matrix.example.com), затем логин и пароль. Есть и другие клиенты (FluffyChat, SchildiChat, Cinny) — все работают с любым Synapse-сервером, протокол один.
Обновление, бэкап и на что смотреть в проде
Обновление Synapse — это обновление образа с последующей миграцией схемы БД, которую контейнер выполняет сам при старте:
docker compose pull synapse
docker compose up -d synapse
docker compose logs -f synapse # проверить, что миграции прошли без ошибок
Перед крупным обновлением стоит сверяться с changelog Synapse — иногда встречаются breaking changes в конфиге.
Бэкап — это две части, и обе обязательны:
# дамп базы
docker compose exec postgres pg_dump -U synapse synapse | gzip > synapse-db-$(date +%F).sql.gz
# файлы: ключ подписи, конфиг, медиахранилище
tar czf synapse-data-$(date +%F).tar.gz synapse-data/
Общие практики резервного копирования docker-контейнеров и то, какие грабли встречаются при бэкапе volume'ов, разобраны в статье про бэкап Docker volume — Synapse здесь не исключение.
Отдельно стоит следить за диском: с открытой федерацией Synapse кэширует медиафайлы из чужих комнат (media_store внутри /data), и этот кэш может расти быстрее, чем ожидаете, если пользователи состоят в больших публичных комнатах. Задача очистки старого кэша настраивается через media_retention в homeserver.yaml, но по умолчанию выключена — включите её, если диск ограничен. Для команды в 20-50 человек без активной федерации в тяжёлые публичные комнаты закладывайте от 2 vCPU / 4 ГБ RAM; с федерацией и большим числом комнат разумнее сразу брать 4 vCPU / 8 ГБ — точные цифры зависят от профиля использования, это ориентир, а не гарантия.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем Synapse отличается от Dendrite?
Dendrite — более новая и лёгкая реализация Matrix-сервера на Go, требует меньше ресурсов, но по состоянию на конец 2026-го уступает Synapse в полноте функций и стабильности при высокой нагрузке. Для продакшн-сервера с реальными пользователями Synapse — более проверенный выбор.
Можно ли обойтись без федерации?
Да: параметр federation_domain_whitelist в homeserver.yaml, оставленный пустым списком, закрывает сервер от общения с внешними серверами.
Нужен ли TURN, если звонков не будет?
Нет, coturn можно не поднимать и убрать блок turn_* из конфига — на текст и файлы это не влияет.
Как перенести сервер без потери ID пользователей?
server_name не привязан к физическому адресу — переносите базу и synapse-data на новый хост и обновляйте .well-known/matrix/server на новый IP.
Почему сообщения не доходят до другого сервера?
Чаще всего дело в неверном .well-known/matrix/server или закрытом 443-м порту — проверить доступность можно через публичный Federation Tester (federationtester.matrix.org).
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →