SSD Nodes Learn Hosting plans →
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-26

Установка Rocket.Chat через Docker Compose на VPS

Разверните Rocket.Chat на собственном сервере с использованием Docker Compose. Узнайте, как настроить обязательный replica set для MongoDB, TLS и резервное копирование данных.

Что вы создаете

Частный командный чат, который полностью принадлежит вам: Rocket.Chat, работающий на вашем собственном VPS через Docker Compose с TLS-терминацией. Все сообщения хранятся в базе данных MongoDB, которую можно резервировать и переносить. Rocket.Chat — это зрелая open-source альтернатива Slack и Teams, поддерживающая каналы, личные сообщения, треды, обмен файлами, а также голосовую и видеосвязь на оборудовании, которое вы арендуете и контролируете. Приложение представляет собой единый контейнер, который запускается за считанные минуты. Все возможные проблемы связаны с базой данных, поэтому большая часть этого руководства посвящена MongoDB и одному требованию, которое часто становится неожиданностью: Rocket.Chat не работает с одиночным экземпляром MongoDB. Ему требуется replica set, даже если этот «набор» состоит всего из одного узла.

Предварительные требования и расчет оперативной памяти, о котором все молчат

Оценивайте ресурсы сервера объективно. Реалистичный минимум для небольшой команды — 2 vCPU и 4 ГБ оперативной памяти. Процесс Node.js для Rocket.Chat потребляет около 1–1.5 ГБ, а кэш WiredTiger в MongoDB по умолчанию забирает примерно половину оставшейся памяти. На VPS с 2 ГБ ОЗУ оба процесса запускаются, но сталкиваются при появлении реального трафика: MongoDB расширяет кэш, Node увеличивает кучу, ядро исчерпывает страницы памяти, и OOM-killer завершает самый ресурсоемкий процесс, обычно mongod. Контейнер выводит Killed, Docker перезапускает его, и в итоге сервер чата падает каждые несколько минут под нагрузкой, которую должен был легко выдерживать. 2 ГБ подходят для ознакомления вдвоем, но не для работы команды. Начинайте с 4 ГБ, а если ожидаете десятки одновременных пользователей, видеозвонки или накопление истории загрузок — выделяйте 8 ГБ.

Перед началом необходимо подготовить три вещи. Доменное имя с 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 на обычный standalone 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, поэтому доступ к нему должен быть только у reverse proxy на том же хосте. Привязка к любому интерфейсу выставила бы страницу входа в открытом виде прямо в публичный интернет. MongoDB вообще не публикуется на хост; она доступна только через внутреннюю сеть Compose под именем mongodb, которое в точности совпадает с именем хоста, используемым MONGO_URL. MONGO_URL содержит ?replicaSet=rs0; если его убрать, драйвер будет воспринимать сервер как standalone, даже если это реплика-сет, и 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}' возвращает compatibleMongoVersions: ["8.0"] для версии 8.5.1, поэтому mongo:8.0 — единственный поддерживаемый движок. Кроме того, документ содержит флаг lts, который показывает, является ли релиз сборкой с долгосрочной поддержкой, которую стоит зафиксировать для сервера, за которым не требуется постоянно следить. Не каждый проект публикует образ с версией. В таком случае версию фиксируют в исходном коде: самостоятельное размещение трекера тренировок openGym означает переключение на конкретный тег git и сборку из него, а не использование ветки, которая постоянно меняется.

Инициализация набора реплик

Запустите стек:

sudo docker compose up -d

Rocket.Chat сразу начнет аварийно завершаться, а Docker будет постоянно перезапускать его. Это ожидаемое поведение, так как набор реплик еще не создан. Создайте его вручную один раз:

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 анонсирует набор реплик под внутренним именем хоста контейнера — случайным хешем, например a1b2c3d4e5f6. Rocket.Chat, подключаясь из своего контейнера, не может разрешить это имя, поэтому драйвер MongoDB выдает ошибку DNS и зацикливается, постоянно записывая в лог MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6. Всегда выполняйте инициализацию с явным указанием имени сервиса, которое соответствует вашему MONGO_URL.

Первый запуск: отслеживание процесса

Как только набор реплик становится основным, следующий перезапуск Rocket.Chat проходит корректно и запускает миграции при первом запуске. Отслеживайте логи:

sudo docker compose logs -f rocketchat

Строка, которую нужно дождаться — это баннер запуска:

+--------------------------------------------+
        SERVER RUNNING
   Rocket.Chat Version: 8.5.1
        NodeJS Version: 22.22.3 - x64
+--------------------------------------------+

Первый запуск происходит медленно, так как приложение выполняет миграции базы данных и строит индексы, поэтому подождите минуту или две, прежде чем беспокоиться. Если в логе вместо этого повторяется MongoServerSelectionError: Server selection timed out after 30000 ms с описанием топологии типа ReplicaSetNoPrimary, значит, набор реплик не инициализирован; если повторяется getaddrinfo ENOTFOUND со случайным хешем, значит, он был инициализирован с неверным хостом. В любом случае вернитесь на шаг назад. Как только вы увидите SERVER RUNNING, Rocket.Chat начнет прослушивать 127.0.0.1:3000, и придет время настроить для него реальное имя хоста и TLS.

Размещение за TLS

Никогда не используйте Rocket.Chat по незашифрованному HTTP. Если вы один раз войдёте в систему через http://, ваш пароль администратора станет доступен любому, кто находится на пути следования трафика. Выполняйте TLS termination на reverse proxy, установленном на том же сервере, и перенаправляйте запросы на 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 — получение TLS-сертификатов Let's Encrypt с помощью 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, чтобы контейнер применил изменения. Если вы хотите, чтобы сервер был доступен только из вашей внутренней сети, а не из публичного Интернета, установите перед ним собственный VPN-шлюз WireGuard на VPS и привяжите прокси к адресу внутри туннеля.

Мастер первоначальной настройки

Перейдите по адресу https://chat.example.com, и Rocket.Chat предложит пройти короткий мастер настройки. Сначала создайте admin account: укажите реальное имя, имя пользователя, адрес электронной почты и надежный пароль. Это единственная учетная запись в системе, поэтому не потеряйте данные для входа. Затем заполните organisation and server info: название организации, сферу деятельности, размер компании, имя сайта и язык по умолчанию. Эти данные носят справочный характер, заполните их и переходите далее. После этого предстоит сделать важный выбор: register this workspace в облаке Rocket.Chat или оставить сервер в режиме standalone.

Регистрация позволяет использовать push-уведомления на мобильных устройствах через шлюз Rocket.Chat и открывает доступ к магазину дополнений, но при этом устанавливается связь с облачной инфраструктурой Rocket.Chat. Режим standalone обеспечивает полную автономность сервера и отсутствие внешних зависимостей, однако push-уведомления для iOS и Android перестанут работать. Это связано с тем, что Apple и Google не позволяют сторонним приложениям использовать собственные сертификаты для push-уведомлений, поэтому официальные клиенты работают только через облачный шлюз. Выбирайте standalone, если конфиденциальность является приоритетом, а пользователи работают преимущественно через веб-интерфейс. Выбирайте регистрацию, если мобильные уведомления критически важны. Вы сможете изменить это решение позже в разделе Admin.

Ограничьте доступ перед тем, как приглашать пользователей

Rocket.Chat поставляется с включенной по умолчанию открытой регистрацией: форма регистрации установлена в Public, поэтому любой, кто узнает URL, сможет создать учетную запись. На публичном хосте это равносильно открытой двери. Перейдите в Admin → Settings → Accounts → Registration и установите для Registration Form значение Disabled, чтобы создавать учетные записи вручную или по ссылке-приглашению, либо выберите Secret URL. Находясь там же, отключите Allow Anonymous Read и Allow Anonymous Write, если вам не требуется публичный канал только для чтения. Если ручное создание каждой учетной записи кажется утомительным, а Rocket.Chat — не единственный сервис, которым пользуется ваша команда, настройте OAuth-авторизацию Rocket.Chat через собственный сервер Authentik SSO. Это позволит управлять доступом новых и увольняющихся сотрудников централизованно, а не для каждого приложения отдельно.

Также определитесь с местом хранения загружаемых файлов. По умолчанию для 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 headers). Убедитесь, что ROOT_URL совпадает с точным публичным адресом, включая https://, и что ваш блок location в nginx устанавливает 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 /swapfile

docker 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 требует Replica Set в MongoDB?

Да, даже для одного сервера с единственным узлом базы данных. Rocket.Chat доставляет сообщения в реальном времени, используя MongoDB change streams, а эта функция доступна только в Replica Set; standalone mongod не может их открыть. Вам не нужно несколько машин: запустите один контейнер MongoDB с флагом --replSet rs0 и инициализируйте набор из одного участника командой rs.initiate(). Если пропустить этот шаг, драйвер не обнаружит primary-узел, и Rocket.Chat будет бесконечно перезапускаться с ошибкой MongoServerSelectionError: Server selection timed out, так и не завершив загрузку.

Сколько оперативной памяти нужно для self-hosted Rocket.Chat?

Планируйте 4 GB как практический минимум и 8 GB для активной команды. Процесс Node в Rocket.Chat потребляет около 1–1.5 GB, а MongoDB забирает примерно половину оставшейся памяти под кэш WiredTiger. На машине с 2 GB эти процессы конфликтуют, и OOM killer завершает работу mongod при любой реальной нагрузке, что отражается в логах как Killed с кодом выхода 137. 2 GB достаточно только для ознакомления с ПО на паре тестовых пользователей.

Как настроить HTTPS для Rocket.Chat?

Запустите reverse proxy на том же VPS, который будет выполнять TLS termination и перенаправлять трафик на 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 перед началом работ.