Как развернуть Rocket.Chat через Docker Compose
Инструкция по установке Rocket.Chat на VPS с использованием Docker Compose. Настройка MongoDB replica set, TLS и резервного копирования для стабильной работы.
Что вы создаете
Вы создаете собственный приватный командный чат: Rocket.Chat, запущенный на вашем VPS через Docker Compose. Соединение защищено TLS, а все сообщения хранятся в базе данных MongoDB, которую можно архивировать и переносить. Rocket.Chat — это зрелая open-source альтернатива Slack и Teams. Она поддерживает каналы, личные сообщения, ветки обсуждений, обмен файлами, а также голосовую и видеосвязь. Все это работает на арендованном вами оборудовании, которое вы полностью контролируете. Приложение представляет собой один контейнер, который запускается за считанные минуты. Основные сложности могут возникнуть при работе с базой данных, поэтому большая часть этого руководства посвящена MongoDB. В частности, здесь описано одно требование, которое часто удивляет новичков: Rocket.Chat не работает с одиночным экземпляром MongoDB. Для работы необходим replica set, даже если этот «set» состоит из одного узла.
Предварительные требования и расчет RAM, о котором вам не говорят
Оцените ресурсы сервера реалистично. Минимальный порог для небольшой команды — 2 vCPU и 4 GB RAM. Процесс Node.js в Rocket.Chat потребляет примерно от 1 до 1.5 GB. По умолчанию кэш MongoDB WiredTiger занимает около половины оставшейся оперативной памяти. На VPS с 2 GB памяти оба процесса запускаются успешно, но сталкиваются при появлении реального трафика: кэш MongoDB растет, куча (heap) Node растет, в ядре заканчиваются страницы памяти, и OOM killer завершает самый крупный процесс — обычно это mongod. Контейнер выдает ошибку Killed, Docker перезапускает его, и вы получаете чат-сервер, который падает каждые несколько минут при нагрузке, с которой он должен справляться. 2 GB достаточно для тестирования вдвоем, но этого недостаточно для командного сервера. Начинайте с 4 GB. Если вы ожидаете десятки одновременных пользователей, видеозвонки или большой объем загружаемых файлов, выделите 8 GB.
Перед началом работы вам также необходимо подготовить три условия. Доменное имя с A-записью, указывающей на публичный IP вашего VPS — функциям реального времени Rocket.Chat и мобильным клиентам требуется стабильное имя хоста, а не голый IP. Порты 80 и 443 должны быть открыты как в брандмауэре сервера, так и в сетевом брандмауэре вашего провайдера (в большинстве панелей это отдельная настройка). И чистый Ubuntu 24.04 KVM VPS с правами root или sudo. Если вы еще не решили, стоит ли запускать чат-сервер в качестве первой службы, ознакомьтесь с руководством по самохостингу в 2026 году, чтобы оценить все компромиссы.
Установка Docker engine и плагина Compose
Используйте официальный apt-репозиторий Docker. Не используйте пакет docker.io из репозиториев Ubuntu или устаревший бинарный файл Python docker-compose. Современный Compose является плагином Docker, который вызывается командой docker compose (через пробел, а не через дефис). Старая версия docker-compose v1 больше не поддерживается и некорректно обрабатывает синтаксис healthcheck и зависимостей, приведенный ниже.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo 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" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginУбедитесь, что оба компонента установлены:
sudo docker version
sudo docker compose versionВажно, чтобы docker compose version выводил результат, похожий на Docker Compose version v2.x. Если возникает ошибка docker: 'compose' is not a docker command, плагин не установлен. Это приведет к трудноотлаживаемым ошибкам в дальнейшем — исправьте проблему сейчас.
Файл compose: MongoDB в качестве реплика-сета из одного узла
В этом месте часто допускают ошибки, поэтому читайте внимательно. Rocket.Chat использует change streams MongoDB для передачи новых сообщений подключенным клиентам в реальном времени. Функция change streams доступна только в реплика-сете. Если указать Rocket.Chat на обычный автономный mongod, он подключится, не сможет открыть change stream и уйдет в бесконечный цикл перезагрузки. Решение простое: запустите один обычный контейнер MongoDB, но используйте флаг --replSet и инициализируйте сет из одного участника.
Создайте рабочую директорию и файл compose.yml:
services:
mongodb:
image: mongo:8.0
restart: always
command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--oplogSize", "128"]
volumes:
- mongodb_data:/data/db
- mongodb_config:/data/configdb
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 10s
retries: 12
rocketchat:
image: registry.rocket.chat/rocketchat/rocket.chat:8.5.1
restart: always
depends_on:
mongodb:
condition: service_healthy
environment:
MONGO_URL: "mongodb://mongodb:27017/rocketchat?replicaSet=rs0"
MONGO_OPLOG_URL: "mongodb://mongodb:27017/local?replicaSet=rs0"
ROOT_URL: "https://chat.example.com"
PORT: "3000"
ports:
- "127.0.0.1:3000:3000"
volumes:
mongodb_data:
mongodb_config:Некоторые решения здесь преднамеренны. Порт Rocket.Chat опубликован на 127.0.0.1:3000, а не на 0.0.0.0 — само приложение не использует TLS, поэтому доступ к нему должен иметь только обратный прокси на той же машине; при привязке ко всем интерфейсам страница входа с открытым текстом окажется в публичном интернете. MongoDB не публикуется на хосте; она доступна только через внутреннюю сеть Compose по имени mongodb, которое является хостнеймом для MONGO_URL. Параметр MONGO_URL включает ?replicaSet=rs0 — без него драйвер будет считать сервер автономным, даже если это реплика-сет, и change streams работать не будут. MONGO_OPLOG_URL указывает на базу данных local, где хранится oplog; современный Rocket.Chat предпочитает change streams, но установка этого параметра безопасна и необходима для совместимости со старым кодом. Для depends_on используется condition: service_healthy, поэтому Compose будет ждать ответа от MongoDB на запрос ping перед запуском Rocket.Chat — для этого и нужен healthcheck.
Используйте фиксированные теги версий для обоих образов — здесь это mongo:8.0 и конкретный релиз Rocket.Chat, например 8.5.1. Никогда не используйте :latest, так как это превращает автоматическое обновление docker pull в случайное обновление, которое невозможно мигрировать. Перед фиксацией версий проверьте текущий стабильный релиз Rocket.Chat и поддерживаемые им версии MongoDB. Rocket.Chat публикует машиночитаемый документ с информацией по каждому релизу: curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}'curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}' возвращает compatibleMongoVersions: ["8.0"] для версии 8.5.1, следовательно, mongo:8.0 — единственный поддерживаемый движок. Также доступен флаг lts, который указывает, является ли данный релиз версией с долгосрочной поддержкой (LTS), подходящей для серверов, требующих минимального обслуживания.
Инициализация replica set
Запустите стек:
sudo docker compose up -dRocket.Chat будет сразу завершать работу с ошибкой, а Docker будет постоянно перезапускать его. Это ожидаемое поведение, так как replica set еще не создана. Создайте её вручную:
sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'Правильный результат — { ok: 1 }. Через несколько секунд единственный узел выберет себя в качестве primary; проверьте это командой:
sudo docker compose exec mongodb mongosh --quiet --eval 'rs.status().members[0].stateStr'Вы должны увидеть PRIMARY. Самая важная деталь на этой странице — аргумент host: "mongodb:27017". Если запустить команду rs.initiate() без списка участников, MongoDB будет анонсировать replica set под внутренним hostname контейнера — случайным хешем вида a1b2c3d4e5f6. Rocket.Chat, подключаясь из своего контейнера, не сможет разрешить это имя. В результате драйвер MongoDB не сможет выполнить DNS-запрос и возникнет бесконечный цикл с ошибкой MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6 в логах. Всегда инициализируйте replica set с явным указанием имени сервиса, которое соответствует вашему MONGO_URL.
Первый запуск: отслеживайте процесс загрузки
После того как set станет primary, следующий перезапуск Rocket.Chat пройдет успешно и начнется выполнение миграций первого запуска. Отслеживайте логи:
sudo docker compose logs -f rocketchatВам нужно дождаться следующей строки (startup banner):
+--------------------------------------------+
SERVER RUNNING
Rocket.Chat Version: 8.5.1
NodeJS Version: 22.22.3 - x64
+--------------------------------------------+Первый запуск занимает много времени — приложение выполняет миграции базы данных и создает индексы. Подождите одну-две минуты, прежде чем предпринимать действия. Если в логах повторяется MongoServerSelectionError: Server selection timed out after 30000 ms с описанием топологии типа ReplicaSetNoPrimary, это означает, что replica set не инициализирована. Если повторяется getaddrinfo ENOTFOUND с произвольным хешем, значит, инициализация прошла с неверным хостом. В обоих случаях вернитесь на один шаг назад. Когда появится SERVER RUNNING, Rocket.Chat начнет прослушивание на 127.0.0.1:3000. После этого можно настраивать реальное имя хоста и TLS.
Используйте TLS
Никогда не открывайте Rocket.Chat через обычный HTTP. Если вы один раз войдете в систему через http://, вы передадите свой пароль администратора любому участнику сетевого трафика. Настройте терминацию TLS на обратном прокси-сервере на той же машине и перенаправляйте трафик на 127.0.0.1:3000. Важны два момента: прокси должен передавать заголовки WebSocket upgrade, так как Rocket.Chat работает в режиме реального времени и не будет функционировать без них; также ROOT_URL контейнера должен точно соответствовать публичному HTTPS-адресу, который вводят пользователи.
Начните с обычного блока сервера nginx в HTTP, который проксирует запросы к приложению и передает заголовки upgrade. Сохраните его как /etc/nginx/sites-available/rocketchat, создайте символьную ссылку в sites-enabled и перезагрузите конфигурацию:
server {
listen 80;
server_name chat.example.com;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Пока оставьте его на порту 80 — блок с listen 443 ssl; без сертификата не пройдет даже sudo nginx -t. Перезагрузите nginx (sudo nginx -t && sudo systemctl reload nginx), затем получите сертификат. Самый простой способ на Ubuntu — сертификаты Let's Encrypt TLS с помощью Certbot и nginx: certbot --nginx перезапишет вышеуказанный блок, добавив listen 443 ssl;, строки ssl_certificate и автоматический редирект с 80 на 443, а также настроит автоматическое продление. Если вы уже используете несколько контейнеров за одним прокси, Traefik с автоматическим TLS для многих Docker-приложений будет более аккуратным вариантом — добавьте метки router и service к сервису rocketchat, и Traefik сам запросит и обновит сертификат без использования блоков nginx. В любом случае установите ROOT_URL в значение https://chat.example.com в compose.yml и снова запустите sudo docker compose up -d, чтобы контейнер применил изменения. Если вы хотите, чтобы сервер был доступен только внутри вашей сети, а не через публичный интернет, используйте self-hosted WireGuard VPN на VPS и привяжите прокси к адресу туннеля.
Мастер первоначальной настройки
Перейдите по адресу https://chat.example.com и следуйте инструкциям Rocket.Chat. Сначала необходимо настроить учетную запись администратора — укажите имя, имя пользователя, email и сложный пароль. Это единственная существующая учетная запись, поэтому не утеряйте ее. Затем заполните информацию об организации и сервере — название, отрасль, размер, имя сайта и язык по умолчанию. Эти данные носят косметический характер, заполните их и продолжайте. Затем следует важный выбор: зарегистрировать рабочее пространство в Rocket.Chat Cloud или использовать автономный режим (standalone).
Регистрация включает мобильные push-уведомления через шлюз Rocket.Chat и маркетплейс дополнений. Это требует установления связи с облачной платформой управления Rocket.Chat. Автономный режим обеспечивает полную конфиденциальность и отсутствие внешних зависимостей, но push-уведомления в iOS и Android перестанут работать. Это происходит потому, что Apple и Google не позволяют самосборным приложениям использовать push-сертификаты; официальные приложения используют облачный шлюз. Выбирайте автономный режим, если приоритетом является конфиденциальность и пользователи работают через веб-интерфейс. Выбирайте регистрацию, если мобильные push-уведомления критически важны. Вы сможете изменить этот выбор позже в разделе Admin.
Настройте безопасность перед приглашением пользователей
В Rocket.Chat по умолчанию включена открытая регистрация — параметр Registration Form установлен в значение Public. Это позволяет любому пользователю, знающему URL, создать учетную запись. На публичном хосте это создает уязвимость. Перейдите в Admin → Settings → Accounts → Registration и установите значение Registration Form на Disabled, чтобы создавать учетные записи вручную или через приглашения, либо на Secret URL. Также рекомендуется отключить параметры Allow Anonymous Read и Allow Anonymous Write, если вам не требуется публичный канал только для чтения.
Также настройте место хранения загружаемых файлов. По умолчанию используется хранилище File Upload типа GridFS, которое сохраняет все изображения и вложения внутри MongoDB. Это удобно, но при этом база данных — и каждый ваш mongodump — будет бесконтрольно расти из-за скриншотов, которые пользователи вставляют в чат. В разделе Admin → Settings → File Upload вы можете изменить тип хранилища на локальную файловую систему или S3-совместимое хранилище, а также установить разумный лимит на максимальный размер файла. Для небольшой команды подходит GridFS, но учитывайте, что объем резервных копий будет постоянно увеличиваться.
Резервное копирование с помощью mongodump
Все ваши данные находятся в томе mongodb_data. Не копируйте том при работающей базе данных — создайте согласованный дамп с помощью mongodump и направьте его в файл на хосте:
sudo docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > rocketchat-$(date +%F).archive.gzЭтот архив в формате gzipped содержит все рабочие данные: пользователей, каналы, сообщения, настройки и — если вы используете GridFS для загрузок — сами файлы. Если вы перенесли загрузки в файловую систему или S3, создайте для них отдельное резервное копирование. Для восстановления на чистую систему сначала инициализируйте replica set, затем выполните:
sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gzСкопируйте архив вне сервера — в объектное хранилище, на другой сервер или любое другое место, где выход из строя VPS не приведет к потере бэкапа. Настройте запуск дампа через cron каждую ночь. Резервная копия, которую вы никогда не восстанавливали, является лишь надеждой, а не гарантией; один раз протестируйте восстановление на тестовом VPS, чтобы убедиться в работоспособности метода до того, как он вам понадобится.
Обновления: фиксируйте теги, изучайте примечания, соблюдайте матрицу совместимости MongoDB
Два правила гарантируют стабильность процесса обновления. Во-первых, обновляйте Rocket.Chat по одной мажорной версии за раз. При запуске выполняются миграции схемы базы данных, поэтому переход через одну мажорную версию невозможен. Если попытаться перейти с 6.x сразу на 8.x, процесс прервется ошибкой миграции, чтобы предотвратить повреждение данных. Измените тег образа на последнюю версию следующей мажорной версии, изучите примечания к релизу на предмет критических изменений, запустите docker compose up -d и дождитесь завершения миграции в логах перед продолжением. Во-вторых, соблюдайте матрицу поддержки MongoDB. Каждая версия Rocket.Chat поддерживает определенный набор версий MongoDB; актуальный список указан в curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions. При обновлении MongoDB — например, с 7.0 до 8.0 — переходите через одну мажорную версию и устанавливайте параметр feature-compatibility version после каждого шага. В MongoDB 8.0 эта команда требует явного указания confirm: true, иначе возникнет ошибка с требованием повторного запуска с флагом подтверждения:
sudo docker compose exec mongodb mongosh --eval 'db.adminCommand({setFeatureCompatibilityVersion: "8.0", confirm: true})'Делайте mongodump перед каждым обновлением любого из компонентов. Это единственный способ обеспечить сохранность данных.
Режимы сбоев и соответствующие строки ошибок
Rocket.Chat уходит в цикл перезагрузки сразу после docker compose up, а docker compose logs rocketchat заполняется ошибкой MongoServerSelectionError. MongoDB запущена, но драйвер не может выбрать primary-узел; текст ошибки указывает на допущенную ошибку. Server selection timed out after 30000 ms с типом топологии ReplicaSetNoPrimary означает, что вы не запустили rs.initiate() — у реплика-сета еще нет конфигурации. getaddrinfo ENOTFOUND с последующим случайным хешем означает, что инициализация была запущена без явного указания host: "mongodb:27017", поэтому MongoDB передала неразрешимое имя хоста контейнера. Используйте sudo docker compose exec mongodb mongosh --eval 'rs.status()' для диагностики: если возникает ошибка MongoServerError: no replset config has been received, инициализируйте сет; если отображается участник, чей name является случайным хешем, повторно инициализируйте сет, используя имя сервиса.
Веб-интерфейс загружается, но процесс входа бесконечно длится и не завершается. Откройте консоль браузера, там будет ошибка WebSocket connection to 'wss://chat.example.com/websocket' failed. Это почти всегда вызвано несоответствием ROOT_URL или прокси-сервером, который не передает заголовки upgrade. Убедитесь, что ROOT_URL совпадает с точным публичным адресом, включая https://, а блок nginx location устанавливает Upgrade и Connection "upgrade" с помощью proxy_http_version 1.1. После изменений выполните docker compose up -d.
Контейнер постоянно завершает работу, и docker compose ps показывает Restarting. docker compose logs обрывается на середине строки, а sudo dmesg | tail показывает Out of memory: Killed process 12345 (mongod) от oom-killer; код выхода — 137. В системе закончилась оперативная память (RAM). Решение — использование VPS большего объема (минимум 4 GB). В качестве временной меры добавьте swap и ограничьте кэш MongoDB с помощью --wiredTigerCacheSizeGB 1 в его command, но swap лишь отсрочит следующий OOM при реальной нагрузке:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfiledocker compose up завершается с ошибкой Error response from daemon: driver failed programming external connectivity ... bind: address already in use. Порт 3000 уже занят — часто это предыдущий контейнер Rocket.Chat, который не был остановлен корректно, или другое приложение. Найдите процесс с помощью sudo ss -ltnp | grep :3000, остановите его или измените хост-сторону маппинга на 127.0.0.1:3001:3000 и обновите proxy_pass вашего прокси-сервера.
FAQ
Действительно ли Rocket.Chat требуется MongoDB replica set?
Да, даже для одного сервера с одним узлом базы данных. Rocket.Chat доставляет сообщения в реальном времени с помощью MongoDB change streams. Функция change streams работает только в режиме replica set; автономный mongod не может ее открыть. Вам не требуется несколько машин; запустите один контейнер MongoDB с флагом --replSet rs0 и инициализируйте набор из одного участника с помощью rs.initiate(). Если пропустить этот шаг, драйвер не сможет найти primary-узел. В результате Rocket.Chat попадет в цикл перезагрузки с ошибкой MongoServerSelectionError: Server selection timed out и не завершит загрузку.
Сколько RAM требуется для self-hosted Rocket.Chat?
Рассчитывайте минимум 4 GB для работы и 8 GB для большой команды. Процесс Node.js в Rocket.Chat потребляет около 1–1.5 GB. MongoDB забирает примерно половину оставшейся RAM под кэш WiredTiger. На системе с 2 GB эти процессы конфликтуют, и OOM killer завершает работу mongod при любой реальной нагрузке. В логах это отображается как Killed с кодом выхода 137. 2 GB достаточно только для ознакомления с ПО с несколькими тестовыми пользователями.
Как настроить HTTPS для Rocket.Chat?
Запустите reverse proxy на том же VPS, чтобы завершить TLS-соединение и перенаправить трафик на 127.0.0.1:3000. Установите переменную ROOT_URL контейнера на ваш публичный адрес https://. Прокси должен передавать заголовки WebSocket upgrade, иначе авторизация будет зависать. Самый простой способ для одного приложения — Certbot с nginx. Traefik удобнее, если вы запускаете несколько контейнеров за одним прокси и хотите автоматического управления сертификатами.
Как сделать резервную копию self-hosted Rocket.Chat?
Используйте mongodump для создания согласованного дампа базы данных вместо копирования volume: docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz. Этот архив содержит пользователей, каналы, сообщения и настройки, а также загруженные файлы, если вы используете GridFS. Скопируйте архив на другой сервер, настройте ежедневное резервное копирование через cron и регулярно проверяйте восстановление с помощью mongorestore на тестовой машине.
Как обновить Rocket.Chat, не повредив MongoDB?
Обновляйте Rocket.Chat по одному мажорному релизу за раз. При запуске выполняются миграции, и пропуск мажорных версий недопустим. Перед обновлением тега образа ознакомьтесь с примечаниями к каждому релизу. Проверьте совместимость версий MongoDB с целевым релизом с помощью curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions. При обновлении MongoDB переходите на одну мажорную версию за раз и устанавливайте setFeatureCompatibilityVersion с помощью confirm: true после каждого шага. Всегда сначала делайте mongodump.