Что такое Matrix и зачем свой сервер#
Matrix - открытый децентрализованный протокол для обмена сообщениями, похожий по духу на работу email. У каждого сервера свой домен, а серверы федерируются между собой, так что пользователи разных серверов могут общаться друг с другом напрямую, без единой центральной компании посередине. Synapse - эталонная (и самая распространенная) реализация сервера этого протокола, разработанная Matrix.org.
Для homelab свой Matrix-сервер решает ту же самую проблему, что и любой другой self-hosted мессенджер: чаты, голосовые и видеозвонки, обмен файлами - но без привязки к чужой инфраструктуре и с полным контролем над данными.
При этом, в отличие от полностью изолированных решений, например Nextcloud Talk, федерация по желанию оставляет возможность общаться и с пользователями других Matrix-серверов, включая крупную публичную сеть matrix.org.
Сам протокол появился в 2014 году - его придумали Мэттью Ходжсон (Matthew Hodgson) и Амандин Ле Пап (Amandine Le Pape), выходцы из корпоративного проекта унифицированных коммуникаций Vector, которым они занимались еще в Amdocs (отсюда и первое название клиента - Vector/Riot, переименованного в Element только в 2018 году). В 2019 году появился отдельный некоммерческий Matrix.org Foundation, который следит за спецификацией протокола отдельно от коммерческой разработки клиента. Самый известный кейс этой истории - ЦЕРН, который в 2019 году начал переводить около 18 000 сотрудников со Skype for Business на свой Matrix/Element именно из-за открытости протокола и возможности полностью держать коммуникации на собственной инфраструктуре. Поэтому это не какой-то вайбкод-проект любителя, а самая что ни на есть энтерпрайзная технология, которая изначально была разработано с учетом корпоративной специфики, но которую мы можем использовать дома.
Synapse - не единственная реализация сервера Matrix (есть более легковесные Dendrite и Conduit, которые возможно для дома были бы более предпочтительны), но именно Synapse самая зрелая реализация и лучше всего документирована, поэтому в статье используется Synapse.
Чем Matrix отличается от Telegram и WhatsApp#
На первый взгляд разница может быть не очевидна - у всех трех решений есть чаты, звонки, группы и мобильные приложения. Но архитектурно это принципиально разные вещи, принципиально.
- Централизация против федерации. У Telegram и WhatsApp все сообщения в конечном счете проходят через серверы одной компании (Telegram и Meta1 соответственно) - других серверов в этих сетях просто не существует.
Matrix - федеративный протокол: сервер может поднять кто угодно, и такие серверы общаются друг с другом напрямую, как почтовые серверы в email. Ваш homelab-сервер - равноправный участник сети, а не клиент чужой инфраструктуры.
Кому принадлежат данные. В Telegram и WhatsApp сообщения (даже если они шифруются на транспорте) физически лежат на серверах вендора. В self-hosted Matrix вся история переписки, медиафайлы и метаданные хранятся там, где вы сами развернули Synapse - в вашей базе Postgres и на вашем диске.
Сквозное шифрование по умолчанию. WhatsApp включает E2E-шифрование для всех чатов по умолчанию. В Telegram оно есть только в отдельных “секретных чатах”, если не ошибаюсь, - обычные чаты и группы шифруются лишь на уровне транспорта до сервера, и сама компания технически может прочитать их содержимое. Хотя тут я не уверен в достоверности моей информации.
В Matrix сквозное шифрование (протокол Olm/Megolm) можно включить для любой комнаты, и на практике клиенты давно включают его по умолчанию для приватных чатов.
Открытый протокол и открытый код. Спецификация Matrix открыта и стандартизирована Matrix.org Foundation, серверная часть (Synapse, Dendrite, Conduit) и клиентская (Element и другие) части - open source, их может проверить и форкнуть кто угодно. Протоколы Telegram и WhatsApp закрытые: клиенты и API контролируют сами компании, а совместимость сторонних реализаций держится на неофициальном реверс-инжиниринге и может быть отключена в любой момент.
Устойчивость к блокировкам. Заблокировать Telegram или WhatsApp - это заблокировать конкретную компанию и ее домены/IP-диапазоны, что уже не раз пытались делать в разных странах. Заблокировать Matrix целиком технически почти невозможно: серверов тысячи, у каждого свой домен, и блокировка одного сервера (даже публичного matrix.org) не трогает остальные - в том числе ваш собственный.
Из этого следует практический вывод: если решающий фактор для вас - удобство и большое количество контактов, ни Telegram, ни WhatsApp Matrix не заменит. Но если важны контроль над данными, независимость от решений одной компании и устойчивость к блокировкам конкретного сервиса - именно для этого нужен собственный Matrix-сервер. При этом вы можете его реализовать как у себя в компании для своих сотрудников, так и просто для своих родственников и близких.
Что понадобится#
- хост с Docker;
- домен и настроенный обратный прокси (в моем случае Traefik) - в статье он используется. Можно обойтись и без него, опубликовав порт напрямую, но это самое плохое решение, которое может быть;
- немного терпения и времени на этапе генерации конфига и секретов - шагов много, но каждый сам по себе достаточно простой.
Для базы данных Synapse будем использовать PostgreSQL, а не встроенный SQLite. Это не вопрос вкуса или удобства. Разработчики прямо предупреждают, что SQLite подходит только для тестов и не годится для реальной эксплуатации из-за ограничений по производительности при параллельном доступе. В переводе на русский, больше двух-трех человек на сервере и целостность SQlite под угрозой.
Шаг 1. Генерируем первоначальный конфиг#
Synapse может сам сгенерировать рабочий homeserver.yaml . Используем один и тот же образ, но с командой generate. Это одноразовый запуск образа только для генерации рабочего конфига, а не постоянный сервис.
Создаем структуру папок:
mkdir -p /home/stilicho/docker/synapse/data
cd /home/stilicho/docker/synapseСоздаем временный файл командой sudo nano docker-compose.yaml только для генерации конфига:
services:
synapse:
image: docker.io/matrixdotorg/synapse:latest
command: generate
environment:
- SYNAPSE_SERVER_NAME=matrix.stilicho.ru
- SYNAPSE_REPORT_STATS=no
volumes:
- ./data:/dataSYNAPSE_SERVER_NAME - это не просто домен, на котором крутится сервис, а имя сервера, которое навсегда будет ассоциироваться с идентификаторами всех ваших пользователей (@user:matrix.stilicho.ru) и адресов комнат. Сменить его после первого запуска нельзя - точнее можно, но только через создание нового сервера. Так что, нет, нельзя. Или да, нельзя. Если сомневаетесь, выбрать домен надо заранее и раз и навсегда, включая то, будет ли это отдельный поддомен (как в этой статье) или голый домен без поддомена, определенным способом связанным через .well-known-делегирование - об этом ниже.
Запускаем генерацию:
docker compose run --rm synapseВ папке data появится готовый homeserver.yaml, а также автоматически сгенерированные matrix.stilicho.ru.log.config и matrix.stilicho.ru.signing.key - последний файл - это криптографический ключ, которым сервер подписывает исходящие федеративные события. Его надо беречь как зеницу ока .
После генерации временный compose-файл больше не нужен. Позже создадим рабочий.
Если, предположим, сервер должен откликаться на домен второго уровня (@user:stilicho.ru), а не на поддомен, технически Synapse все равно должен физически работать на отдельном поддомене (например, на том же matrix.stilicho.ru) - а голый домен нужно “делегировать” ему через два файла: https://stilicho.ru/.well-known/matrix/server и https://stilicho.ru/.well-known/matrix/client, отдающие JSON с адресом реального сервера. Это отдельная тема, в настоящей статье я ее не разбираю - для первого сервера проще и надежнее сразу использовать поддомен как есть.
Шаг 2. Правим сгенерированный homeserver.yaml#
Открываем data/homeserver.yaml и вносим несколько правок.
Переключаемся на PostgreSQL . Как я писал выше, по умолчанию сгенерированный конфиг использует SQLite, поэтому удаляем упоминание sqlite и вставляем указанную ниже часть:
database:
name: psycopg2
txn_limit: 10000
args:
user: synapse
password: "замените_на_пароль_из_env"
dbname: synapse
host: synapse_db
port: 5432
cp_min: 5
cp_max: 10host: synapse_db- это имя сервиса Postgres из compose-файла (см. шаг 3), Docker DNS резолвит его автоматически внутри общей сети, никакого дополнительного IP прописывать не нужно.password- должен буквально совпадать со значениемPOSTGRES_PASSWORDиз.envфайла compose-стека - это два независимых места, где хранится один и тот же пароль, и их рассинхронизация - самая частая причина, по которой Synapse не может подключиться к базе после первого запуска.
Отключаем открытую регистрацию - по умолчанию в сгенерированном файле ключ вообще отсутствует, поэтому это надо прописать явно:
enable_registration: falseБез этого значения любой человек, узнавший адрес вашего сервера, сможет сам зарегистрировать себе аккаунт на вашем сервере. Пользователей на домашнем сервере будем создавать вручную, отдельной командой (см. шаг 6).
В сгенерированном файле уже есть три секрета: registration_shared_secret, macaroon_secret_key и form_secret. Synapse генерирует их случайным образом, при первом запуске - в отличие от готовых демо-конфигов, которые иногда гуляют по интернету (например, в чужих репозиториях с примерами docker-compose), где эти значения могут быть одними и теми же у всех, кто скопировал файл не глядя. Если вы все же переносите конфиг из чужого примера - обязательно сгенерируйте свои значения заново, например командой openssl rand -hex 32, и не используйте то, что было написано в примере изначально.
Остальные ключи (server_name, pid_file, listeners, media_store_path, signing_key_path, trusted_key_servers) сгенерированы верно и трогать их не нужно.
Шаг 3. Финальный docker-compose.yaml#
services:
synapse:
image: docker.io/matrixdotorg/synapse:latest
container_name: matrix-synapse
restart: unless-stopped
environment:
- TZ=${TZ}
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
volumes:
- ./data:/data
depends_on:
- synapse_db
networks:
- synapse
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.synapse.entrypoints=web"
- "traefik.http.routers.synapse.rule=Host(`matrix.stilicho.ru`)"
- "traefik.http.middlewares.synapse-https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.synapse.middlewares=synapse-https-redirect"
- "traefik.http.routers.synapse-secure.entrypoints=websecure"
- "traefik.http.routers.synapse-secure.rule=Host(`matrix.stilicho.ru`)"
- "traefik.http.routers.synapse-secure.tls=true"
- "traefik.http.routers.synapse-secure.tls.certresolver=cloudflare"
- "traefik.http.routers.synapse-secure.service=synapse"
- "traefik.http.services.synapse.loadbalancer.server.port=8008"
- "traefik.docker.network=proxy"
synapse_db:
image: docker.io/postgres:16-alpine
container_name: matrix-postgres
restart: unless-stopped
environment:
- TZ=${TZ}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- ./postgres:/var/lib/postgresql/data
networks:
- synapse
networks:
synapse:
proxy:
external: trueДавайте разберем отличия от того, что генерирует сам Synapse “из коробки” в примерах из интернета:
networks: synapseиproxy-synapse- внутренняя сеть только для связи с Postgres, создается автоматически;proxy- внешняя сеть Traefik (external: true), должна существовать заранее (docker network create proxy, если еще не создана под другие сервисы). Базаsynapse_dbсидит только в сетиsynapse- снаружи она никому, кроме самого Synapse, не нужна.labels- тот же паттерн, что и в остальных статьях сайта с использованием Traefik в качестве обраного прокси: HTTP-роутер с редиректом на HTTPS, HTTPS-роутер с TLS-резолверомcloudflare(замените на имя своего резолвера, если оно другое) и балансировщик на внутренний порт8008контейнера - именно на нем Synapse слушает HTTP по умолчанию.POSTGRES_INITDB_ARGS- принудительно задает кодировку и сортировку базы вC/UTF-8при первой инициализации. Это важно сделать именно на старте../postgres:/var/lib/postgresql/data- bind mount вместо именованного volume, по тому же принципу, что и в остальных мои примерах развертывания сервисов. Путь на хосте прописан явно, так проще делать бэкапы.
.env-файл рядом с compose-файлом:
TZ=Europe/Moscow
POSTGRES_USER=synapse
POSTGRES_PASSWORD=замените_на_свой_парольЗначение POSTGRES_PASSWORD здесь должно быть точно таким же, как то, что вы прописали в password внутри database.args в homeserver.yaml на шаге 2. Не коммитьте .env в git - если ведете конфиги через GitOps (см. статью про Forgejo и Komodo), добавьте .env в .gitignore.
Поднимаем стек:
docker compose up -dПервый запуск Postgres инициализирует структуру данных, Synapse может перезапуститься один-два раза, пока база не станет доступна - это нормально.
Шаг 4. Проверяем, что сервер поднялся#
Открываем https://matrix.stilicho.ru в браузере - Synapse отдает простую страницу с текстом вроде “Welcome to the Matrix”, это и есть признак того, что сервер работает и Traefik успешно до него достучался.
Более наглядная проверка - через федеративный tester от самого проекта Matrix.org: https://federationtester.matrix.org/, вводим туда matrix.stilicho.ru - если все настроено верно (включая DNS и сертификат), тест покажет зеленый статус федерации, даже если вы пока не планируете общаться с внешними серверами.
Шаг 5. Ротация логов#
По умолчанию log_config, сгенерированный на шаге 1, настроен на ротацию по размеру, но всегда полезно свериться с реальным содержимым файла data/matrix.stilicho.ru.log.config - там задается, помимо прочего, уровень логирования (INFO по умолчанию - для домашнего сервера более чем достаточно, DEBUG быстро разрастется в гигабайты бесполезных для домашнего пользователя данных).
Шаг 6. Создаем первого пользователя#
Раз открытая регистрация отключена (шаг 2), пользователей создаем вручную, встроенным скриптом внутри контейнера:
docker exec -it matrix-synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008Скрипт спросит логин, пароль и - отдельным вопросом - права администратора. Права администратора имеет смысл давать только себе; остальным пользователям они не нужны для обычного общения.
Под капотом скрипт использует тот самый registration_shared_secret из homeserver.yaml - именно поэтому регистрация через эту команду работает, даже когда строка конфигаenable_registration: false запрещает регистрацию всем остальным.
Шаг 7. Подключаем клиент#
Официальный клиент - Element (веб, десктоп для Windows/macOS/Linux, мобильные приложения для Android/iOS). При первом входе в любой версии клиента:
- Выбираем Sign in.
- Меняем домен сервера по умолчанию (
matrix.org) на свой - обычно есть кнопка вроде Edit рядом с полем адреса сервера. - Указываем
https://matrix.stilicho.ru. - Вводим логин и пароль, созданные на шаге 6.
С этого момента сервер и клиент привязаны друг к другу, можно пользоваться и веб-версией, и десктопным, и мобильным клиентом одновременно, под одним и тем же аккаунтом.
Что дальше#
Показанная здесь схема - минимальная рабочая установка. Дальше есть куда расти:
- Голосовые и видеозвонки. Тема оказалась настолько объемной, что я вынес ее в отдельную статью - «Голосовые и видеозвонки в Matrix - разворачиваем LiveKit и Element Call». Спойлер: обычный TURN-сервер (coturn) уже не главный путь к звонкам - современные клиенты вроде Element X перешли на архитектуру MatrixRTC с LiveKit-бэкендом;
- Мосты (bridges) в другие мессенджеры (Telegram, WhatsApp, Discord) - позволяют читать и писать в чужие сети прямо из Matrix-клиента;
- Media repository отдельным сервисом, если начнет расти объем пересылаемых файлов и изображений;
- Резервное копирование - как минимум папки
data(конфиг, ключи, медиа) и дампов Postgres; про сами инструменты бэкапа - в отдельных статьях на сайте.
Но для первого знакомства с собственным Matrix-сервером всего показанного выше, с моей точки зрения, достаточно. Рабочий сервер за Traefik, безопасные секреты, отключенная открытая регистрация и подключенный клиент.
Ссылки#
Организация признана экстремистской и ее деятельность запрещена на территории РФ. ↩︎





