MAATRIX / Блог / Rocket.Chat в Docker Compose: готовый файл

Rocket.Chat в Docker Compose: готовый файл

MAATRIX

Mattermost — не единственный self-hosted мессенджер, и не всегда самый удобный. Если вам нужны встроенные видеозвонки без отдельного сервера Jitsi, гибкие омниканальные интеграции (Telegram, WhatsApp Business API, email в чат) и более живое сообщество плагинов — стоит присмотреться к Rocket.Chat. Разворачивается он тоже через Docker Compose, но с одной особенностью: MongoDB здесь обязана работать как replica set, даже если у вас один сервер и один узел базы. Ниже — рабочий compose-файл, объяснение зачем нужен реплика-сет из одного узла, и список граблей, на которых спотыкаются почти все при первом запуске.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Почему Rocket.Chat требует MongoDB replica set

Rocket.Chat использует change streams MongoDB для realtime-обновлений (новые сообщения, статусы прочтения, присутствие пользователей приходят через них, а не через поллинг). Change streams — фича, которая работает только поверх replica set, обычная standalone-инсталляция MongoDB её не поддерживает в принципе.

Это не значит, что вам нужно поднимать три сервера с MongoDB. Реплика-сет из одного узла (single-node replica set) — штатная конфигурация: тот же движок, тот же протокол, просто без резервных участников. Для нагрузки в десятки-сотни одновременных пользователей его достаточно. Если команда вырастет за пару тысяч активных пользователей одновременно — тогда имеет смысл вынести MongoDB на отдельный сервер и добавить второй узел реплика-сета для отказоустойчивости, но это отдельная задача, не блокер для старта.

Сама инициализация реплика-сета — отдельный шаг после первого запуска контейнера, автоматически Rocket.Chat её не сделает. Об этом ниже.

Готовый docker-compose.yml

Структура: контейнер Rocket.Chat, контейнер MongoDB и вспомогательный контейнер mongo-init-replica, который инициализирует реплика-сет один раз и завершается.

version: "3.8"

services:
  rocketchat:
    image: rocket.chat:6.10.2
    restart: unless-stopped
    environment:
      MONGO_URL: "mongodb://mongo:27017/rocketchat?replicaSet=rs0"
      MONGO_OPLOG_URL: "mongodb://mongo:27017/local?replicaSet=rs0"
      ROOT_URL: "https://chat.example.com"
      PORT: 3000
      DEPLOY_METHOD: docker
    depends_on:
      - mongo
    ports:
      - "127.0.0.1:3000:3000"
    volumes:
      - rocketchat_uploads:/app/uploads
    networks:
      - rocketchat_net

  mongo:
    image: mongo:6.0
    restart: unless-stopped
    volumes:
      - mongo_data:/data/db
      - mongo_config:/data/configdb
    command: mongod --replSet rs0 --oplogSize 128 --bind_ip_all
    networks:
      - rocketchat_net

  mongo-init-replica:
    image: mongo:6.0
    restart: "no"
    depends_on:
      - mongo
    networks:
      - rocketchat_net
    entrypoint: >
      bash -c "
        sleep 10 &&
        mongosh mongo/rocketchat --eval \"
          rs.initiate({
            _id: 'rs0',
            members: [{ _id: 0, host: 'mongo:27017' }]
          })
        \"
      "

volumes:
  rocketchat_uploads:
  mongo_data:
  mongo_config:

networks:
  rocketchat_net:
    driver: bridge

Что поправить под себя:

  • ROOT_URL — обязательно реальный домен с https://, иначе ссылки в уведомлениях и загрузка файлов будут ломаться.
  • Версию образа rocket.chat:6.10.2 лучше зафиксировать конкретным тегом, а не latest — так апгрейды становятся управляемыми, а не случайными при пересоздании контейнера.
  • Порт 3000 привязан только к 127.0.0.1 — наружу его отдаёт обратный прокси с TLS, напрямую в интернет контейнер лучше не светить.

Запуск:

mkdir -p ~/rocketchat && cd ~/rocketchat
nano docker-compose.yml   # вставить содержимое выше
docker compose up -d

Контейнер mongo-init-replica отработает один раз, инициализирует реплика-сет и остановится — это нормально, docker compose ps покажет его в статусе Exited (0). Если увидите там ошибку — почти всегда дело в том, что MongoDB ещё не успела подняться за 10 секунд паузы (см. раздел про грабли).

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Обратный прокси и TLS

Rocket.Chat слушает HTTP на 3000 порту и не умеет TLS сам — сертификат и HTTPS-терминацию берёт на себя прокси перед ним. Дополнительно нужно правильно прокинуть WebSocket, иначе realtime-обновления просто не будут приходить, а чат превратится в почтовый клиент с ручным обновлением страницы.

Пример конфига для nginx:

server {
    listen 443 ssl http2;
    server_name chat.example.com;

    ssl_certificate     /etc/letsencrypt/live/chat.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;

    client_max_body_size 200m;

    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-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name chat.example.com;
    return 301 https://$host$request_uri;
}

Ключевая строка — proxy_set_header Connection "upgrade" вместе с proxy_http_version 1.1. Без неё nginx закрывает соединение как обычный HTTP-запрос, WebSocket-хендшейк не проходит, и в консоли браузера вы увидите бесконечные reconnect-попытки.

Если предпочитаете автоматический TLS без ручного продления сертификатов, конфигурация с Traefik или Caddy тоже подходит — оба умеют проксировать WebSocket из коробки при базовых лейблах/директивах. Как выбрать между ними, разобрано в статье Traefik или Nginx Proxy Manager: что выбрать для сервера. Про сам механизм выпуска сертификатов — в статье как установить и настроить Let's Encrypt SSL на VPS.

Первый запуск и создание администратора

После docker compose up -d и успешной инициализации реплика-сета откройте https://chat.example.com — Rocket.Chat покажет мастер первоначальной настройки: имя организации, размер команды, создание первого администратора. Это разовый веб-визард, доступный только пока в системе нет ни одного пользователя.

Если визард не открывается, а вместо него белый экран или ошибка соединения — проверьте логи:

docker compose logs -f rocketchat

Частая причина на этом этапе — Rocket.Chat запустился раньше, чем реплика-сет MongoDB перешёл в статус PRIMARY. Контейнер уйдёт в цикл перезапуска с ошибкой подключения к MongoDB. В этом случае просто:

docker compose restart rocketchat

Через несколько секунд после того, как MongoDB стабилизируется, Rocket.Chat поднимется штатно.

Ресурсы, лимиты и бэкапы

Rocket.Chat на связке с MongoDB заметно прожорливее статичного сайта: под небольшую команду (до ~50 активных пользователей) закладывайте минимум 2 vCPU и 4 ГБ RAM, из которых MongoDB съест значительную часть на индексы и оперативный кэш. При росте команды и истории сообщений в первую очередь упрётесь в диск — вложения и медиафайлы растут быстро, если не настроить внешнее файловое хранилище (S3-совместимое) в админке.

Бэкапить нужно два тома: mongo_data (вся переписка, пользователи, настройки) и rocketchat_uploads (файлы, если храните их локально, а не в S3). Дамп MongoDB проще всего снимать штатным mongodump:

docker compose exec mongo mongodump --uri="mongodb://localhost:27017/rocketchat?replicaSet=rs0" --archive=/data/db/rocketchat-$(date +%F).archive
docker cp $(docker compose ps -q mongo):/data/db/rocketchat-$(date +%F).archive ./backups/

Для регулярных автоматических бэкапов Docker-томов на сервере разумнее не изобретать велосипед, а использовать BorgBackup — про его установку и подключение к volume есть отдельная статья: BorgBackup в Docker Compose: готовый файл. Общие грабли при бэкапе именно Docker-томов разобраны в статье бэкап Docker volume на сервере: частые ошибки и решения.

Обновление версии

Обновление — это смена тега образа и пересоздание контейнера, но с одной оговоркой: перед мажорным апгрейдом (например, с 6.x на 7.x) стоит сделать полный бэкап MongoDB, потому что миграции схемы базы иногда необратимы без отката из дампа.

# в docker-compose.yml поменять тег
# image: rocket.chat:6.10.2 -> image: rocket.chat:6.11.0

docker compose pull rocketchat
docker compose up -d rocketchat
docker compose logs -f rocketchat

При первом старте после обновления Rocket.Chat сам прогоняет миграции базы — это может занять от нескольких секунд до пары минут в зависимости от объёма данных, процесс виден в логах строками вида Update to version X.Y.Z. Прерывать контейнер в этот момент нельзя.

Частые проблемы при развёртывании

Практика показывает, что почти все проблемы с Rocket.Chat в Docker сводятся к небольшому набору причин.

СимптомВероятная причинаЧто проверить
Контейнер rocketchat в рестарт-лупMongoDB ещё не PRIMARYdocker compose logs mongo, статус rs.status()
Realtime не работает, сообщения появляются только после F5Прокси не пробрасывает WebSocketзаголовки Upgrade/Connection в конфиге прокси
Ошибка при инициализации реплика-сетаmongo-init-replica стартовал раньше, чем поднялась MongoDBувеличить sleep перед rs.initiate() до 15-20 секунд
Файлы не загружаются, ошибка при аплоадеПревышен client_max_body_size в nginx или лимит в самом Rocket.Chatзначение в конфиге прокси и в Admin → File Upload
После рестарта сервера MongoDB не поднимаетсяПовреждён mongo_config том из-за resource limit / OOMdocker compose logs mongo, наличие свободной RAM

Общие принципы работы с Docker Compose в проде — таймауты health-check, порядок запуска сервисов, restart policy — подробно разобраны в статье Docker Compose для продакшена: пошаговая установка и в статье про частые ошибки на эту тему.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли запустить Rocket.Chat без replica set MongoDB?

Технически контейнер стартует и с обычной MongoDB, но realtime-функции (новые сообщения без перезагрузки, статусы онлайн) работать не будут — они завязаны на change streams, которые требуют replica set. Смысла в такой инсталляции немного.

Сколько ресурсов нужно для 100 пользователей?

Ориентировочно от 4 vCPU и 8 ГБ RAM на сервер, где Rocket.Chat и MongoDB работают вместе — но точная цифра сильно зависит от активности переписки, количества интеграций и того, храните ли вы вложения локально или в S3. Проверяйте нагрузку по факту через docker stats.

Чем Rocket.Chat отличается от Mattermost в плане развёртывания?

Главное отличие — обязательный replica set MongoDB у Rocket.Chat против обычной PostgreSQL у Mattermost, это чуть усложняет compose-файл. В остальном оба разворачиваются похожим образом, за прокси с TLS. Если сомневаетесь, какой мессенджер выбрать, сравнение развёртывания Mattermost есть в статье как установить и настроить Mattermost на VPS.

Как перенести Rocket.Chat на другой сервер?

Снять mongodump с исходного сервера, поднять точно такой же compose-стек на новом, дождаться инициализации реплика-сета, остановить Rocket.Chat и восстановить базу через mongorestore, затем скопировать том с загрузками. DNS домена переключить на новый IP уже после проверки, что всё поднялось корректно.

Нужен ли отдельный сервер под MongoDB?

Для команды до нескольких сотен активных пользователей — нет, один сервер с обоими контейнерами справляется. Вынос MongoDB на отдельную машину имеет смысл, когда нужно масштабировать реплика-сет до нескольких узлов ради отказоустойчивости, а не только производительности.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →