Корпоративные мессенджеры за последние десять лет проникли во все уголки рабочего пространства - от стартапов из трёх человек до огромных международных холдингов. И каждая команда рано или поздно сталкивается с одним и тем же выбором. Платить за подписку популярному облачному сервису, отправлять туда обсуждения проектов, скриншоты с чувствительными данными и пароли в личках, а потом каждый раз вздрагивать при новостях о массовой утечке. Или поднимать собственный сервер, где переписка остаётся под полным контролем своей же организации, но требует чуть больше внимания при первичной настройке.
Среди свободных самохостимых решений Let's Chat занимает интересную нишу. Это не громоздкий платформенный комбайн с тысячами возможностей, а легковесный мессенджер, специально заточенный под небольшие плотные команды. Серверная часть написана на Node.js, в качестве хранилища используется MongoDB. Получился аккуратный продукт без лишнего веса, который разворачивается на скромном сервере и при этом покрывает основные потребности группового общения.
Из заметных возможностей - аутентификация через LDAP и Kerberos, что критично для интеграции с уже существующей корпоративной инфраструктурой. Есть REST-подобный API для интеграций с внешними системами и собственными ботами. Поддерживается XMPP, что даёт возможность подключаться через сторонние клиенты. И базовый функционал на месте - комнаты по интересам, голосовые стикеры, эмодзи, поиск по истории сообщений.
Ниже разобран полный путь установки на Ubuntu 22.04 - от подготовки Node.js до открытия веб-интерфейса в браузере. Параллельно настраивается MongoDB как хранилище и Nginx в роли обратного прокси, что переводит мессенджер с нестандартного порта 5000 на привычный 80-й.
Подготовительные требования и базовое окружение для развёртывания собственного мессенджера
Чтобы пройти этот путь без сюрпризов, понадобится сервер на Ubuntu 22.04. Доступ нужен с правами root или через пользователя с возможностью повышения через sudo. В оригинальной инструкции команды приведены без префикса sudo, и в этой адаптации сохранён тот же стиль - подразумевается работа из-под root напрямую. При работе под обычным пользователем перед каждой командой нужно ставить sudo.
Ресурсы для Let's Chat скромные. Минимум 1 ГБ памяти, лучше 2 ГБ для комфортной работы с MongoDB параллельно. Дисковое пространство - порядка 5-10 ГБ под систему, дистрибутив, базу и место для роста истории сообщений. Для команды из 10-20 человек этого хватит надолго.
Установка свежей версии Node.js через официальный репозиторий NodeSource
Let's Chat работает на платформе Node.js, и без неё ничего не запустится. В стандартном репозитории Ubuntu 22.04 живёт относительно старая версия Node, для современных проектов недостаточная. Поэтому подключаем официальный репозиторий NodeSource - один из самых проверенных источников свежих сборок Node для Debian-подобных систем.
Сначала ставим зависимости, без которых корректно подключить новый репозиторий не получится:
apt install dirmngr gnupg apt-transport-https ca-certificates software-properties-common -y
Разберём каждый пакет. Утилита dirmngr нужна для работы с серверами ключей. Пакет gnupg - это инструменты GNU Privacy Guard для работы с подписями. apt-transport-https разрешает apt скачивать пакеты по защищённому протоколу. ca-certificates содержит корневые сертификаты для проверки подписи. software-properties-common даёт удобные утилиты вроде add-apt-repository.
Подключаем репозиторий NodeSource через специальный установочный скрипт:
curl -sL https://deb.nodesource.com/setup_18.x | bash -
Скрипт сам определяет версию Ubuntu, добавляет нужный репозиторий и подписной ключ, обновляет индекс пакетов. Цифра 18 в URL - это мажорная версия Node.js. Можно использовать 16, 20 или другую LTS-версию, но 18 - проверенный временем релиз с хорошей совместимостью с большинством проектов.
Ставим саму платформу:
apt-get install nodejs -y
Пакет nodejs из NodeSource содержит и саму среду исполнения, и менеджер пакетов npm в комплекте.
Проверяем результат:
node -v
Ожидаемый вывод:
v18.7.0
Конкретный номер версии будет отличаться - зависит от того, когда обновлялся пакет в репозитории NodeSource. Главное - что Node 18 успешно встал и отвечает.
Установка MongoDB в качестве основного хранилища данных мессенджера
Let's Chat хранит сообщения, профили пользователей, метаданные комнат в MongoDB - документной базе данных, идеально подходящей для подобных задач. Гибкая структура документов позволяет хранить разнородные данные без жёсткой схемы, что удобно для развивающегося приложения.
Сначала разберёмся с одной особенностью Ubuntu 22.04. MongoDB версии 4.4 (которую ставим) требует библиотеку libssl1.1, а в Ubuntu 22.04 уже идёт libssl3 по умолчанию. Нужная старая версия отсутствует в стандартных репозиториях, поэтому скачиваем её напрямую с архива Ubuntu:
wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb
Первая команда скачивает deb-пакет напрямую из архива более старого Ubuntu. Вторая устанавливает его через dpkg в обход обычного механизма apt. Это не самое элегантное решение, но рабочее. Альтернативой было бы поставить более свежую MongoDB 6.0 или 7.0, которые умеют работать с libssl3 - но в этом руководстве сохранён исходный сценарий с 4.4 версией.
Подключаем GPG-ключ MongoDB и добавляем репозиторий:
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
В URL репозитория стоит обратить внимание на сегмент focal - это кодовое имя Ubuntu 20.04. Репозиториев под Ubuntu 22.04 (jammy) для MongoDB 4.4 не существует, поэтому используется ближайший по версии. Это работает благодаря бинарной совместимости между релизами Ubuntu.
Обновляем индекс пакетов:
apt update
Ставим MongoDB:
apt install -y mongodb-org
Метапакет mongodb-org потянет за собой все компоненты - сервер, утилиты администратора, инструменты импорта-экспорта и другие.
Запускаем службу и включаем автозапуск:
systemctl start mongod
systemctl enable mongod
Первая команда поднимает базу прямо сейчас, вторая прописывает запуск при загрузке системы. Без enable после перезагрузки MongoDB останется в покое, и Let's Chat не сможет подключиться к хранилищу.
Проверяем статус:
systemctl status mongod
Ожидаемый вывод:
? mongod.service - MongoDB Database Server
Loaded: loaded (/lib/systemd/system/mongod.service; disabled; vendor preset: enabled)
Active: active (running) since Sun 2022-07-31 04:00:08 UTC; 6s ago
Docs: https://docs.mongodb.org/manual
Main PID: 42274 (mongod)
Memory: 61.1M
CPU: 1.014s
CGroup: /system.slice/mongod.service
??42274 /usr/bin/mongod --config /etc/mongod.conf
Jul 31 04:00:08 ubuntu2204 systemd[1]: Started MongoDB Database Server.
Строка active (running) подтверждает, что база поднялась корректно. Память 61 МБ - совсем скромный аппетит для пустой базы, цифра будет расти по мере накопления сообщений и пользователей. Файл /etc/mongod.conf содержит основные настройки сервера, и при необходимости его можно править для тонкой настройки производительности или включения авторизации.
Загрузка исходников Let's Chat и установка Node.js зависимостей через npm
Let's Chat распространяется через GitHub - дистрибутивных пакетов в репозиториях нет. Подход типичный для большинства Node-приложений: клонировать репозиторий, поставить зависимости, запустить.
Клонируем репозиторий:
git clone https://github.com/sdelements/lets-chat.git
После клонирования в текущем каталоге появится папка lets-chat со всеми исходниками проекта. Заходим в неё и ставим зависимости:
cd lets-chat
npm install
Команда npm install читает файл package.json, видит там список нужных пакетов и автоматически выкачивает их в подкаталог node_modules. Процесс может занять несколько минут на медленном интернете и сотни мегабайт дискового пространства - такова специфика экосистемы Node, где даже простые проекты тянут за собой десятки и сотни зависимостей.
В процессе могут появиться предупреждения о устаревших пакетах или о потенциальных уязвимостях - это нормально для проекта, который активно не развивается уже несколько лет. На функциональность это обычно не влияет, но в боевой эксплуатации стоит регулярно сверяться с обновлениями и пробовать обновлять зависимости через npm audit fix.
Копируем шаблон конфигурации в реальный конфиг и запускаем сервер для проверки:
cp settings.yml.sample settings.yml
npm start
Файл settings.yml содержит все настройки приложения - адрес и порт, параметры подключения к MongoDB, режимы аутентификации, лимиты на размеры сообщений и многое другое. На этапе пробного запуска используем дефолтные значения.
Ожидаемый вывод при старте:
> lets-chat@0.4.8 start
> node app.js
(node:42373) DeprecationWarning: `open()` is deprecated in mongoose >= 4.11.0, use `openUri()` instead, or set the `useMongoClient` option if using `connect()` or `createConnection()`. See http://mongoosejs.com/docs/4.x/docs/connections.html#use-mongo-client
(Use `node --trace-deprecation ...` to show where the warning was created)
???
?????????????????????????
??????????
??? ?????? ?????????
???
?????????????????????????
???????????
????????????????????
???
??????
???
????????
???
????????????????
???
???
??????
???
????????
???
????????????????
???
????????????????
???
????????
???????????
??????
???
???
????????????????
???
????????
??????????
??????
???
???
Release 0.4.8
Странные знаки вопроса - это попытка терминала отобразить ASCII-арт логотипа Let's Chat, который не может корректно вывести символы псевдографики. На функциональность не влияет, в браузере всё будет в порядке. Появление строки Release 0.4.8 означает, что сервер успешно стартовал и слушает свой порт.
Предупреждение про DeprecationWarning связано с использованием устаревшего API библиотеки mongoose. Это историческое наследие проекта и тоже не сказывается на работе - просто разработчики ещё не переписали код на актуальное API.
Останавливаем сервер через Ctrl + C - дальше его нужно оформить как нормальную службу.
Создание systemd юнита для устойчивой работы службы Let's Chat
Запуск через npm start работает, но в боевой эксплуатации не годится. Стоит закрыть SSH-сессию или перезагрузить сервер - и мессенджер пропадёт. Решение классическое - оформить запуск через systemd, чтобы служба поднималась автоматически при загрузке и перезапускалась при сбоях.
Создаём юнит-файл:
nano /etc/systemd/system/letschat.service
Содержимое:
[Unit]
Description=Let's Chat Server
Wants=mongodb.service
After=network.target mongod.service
[Service]
Type=simple
WorkingDirectory=/root/lets-chat
ExecStart=/usr/bin/npm start
User=root
Group=root
Restart=always
RestartSec=9
[Install]
WantedBy=multi-user.target
Разберём по секциям. В блоке Unit описаны зависимости службы. Параметр Wants=mongodb.service говорит, что MongoDB желательна для работы Let's Chat. After=network.target mongod.service задаёт порядок запуска - сначала поднимается сеть и база, потом сам мессенджер. Без этого Let's Chat мог бы стартовать раньше, чем доступна MongoDB, и упасть при попытке подключения.
Секция Service содержит параметры запуска. Type=simple означает, что команда из ExecStart запускает сам процесс службы (без форка в фон). WorkingDirectory указывает рабочий каталог - именно из него будет запускаться npm. ExecStart - сама команда запуска. User и Group выставлены в root, что не лучшая практика безопасности. В реальной эксплуатации стоит создать отдельного системного пользователя и переместить установку в /opt или /home, но для простоты примера оставлено как в оригинале.
Параметры Restart=always и RestartSec=9 настраивают автоматический перезапуск при сбоях. Если процесс упал по любой причине, systemd подождёт 9 секунд и поднимет его заново. Без этого даже маленькая ошибка в коде или временная проблема с базой превратилась бы в полное падение сервиса.
Сохраняем файл и перечитываем конфигурацию systemd:
systemctl daemon-reload
Эта команда обязательна после любых изменений в юнит-файлах. Без неё systemd работает по старому кешированному набору, и новые правки игнорируются.
Запускаем службу и включаем автозапуск:
systemctl start letschat
systemctl enable letschat
Проверяем статус:
systemctl status letschat
Ожидаемый вывод:
? letschat.service - Let's Chat Server
Loaded: loaded (/etc/systemd/system/letschat.service; disabled; vendor preset: enabled)
Active: active (running) since Sun 2022-07-31 04:02:34 UTC; 4s ago
Main PID: 42429 (npm start)
Tasks: 23 (limit: 2242)
Memory: 157.9M
CPU: 4.457s
CGroup: /system.slice/letschat.service
??42429 "npm start" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
??42440 sh /tmp/start-b5e830e4.sh
??42441 letschat "" "" ""
Jul 31 04:02:34 ubuntu2204 npm[42429]: > node app.js
Jul 31 04:02:38 ubuntu2204 npm[42441]: (node:42441) DeprecationWarning: `open()` is deprecated in mongoose >= 4.11.0, use `openUri()` instead>
Jul 31 04:02:38 ubuntu2204 npm[42441]: (Use `node --trace-deprecation ...` to show where the warning was created)
Jul 31 04:02:38 ubuntu2204 npm[42441]: ???
?????????????????????????
??????????
??? ?????? ?????????
Jul 31 04:02:38 ubuntu2204 npm[42441]: ???
?????????????????????????
???????????
????????????????????
Jul 31 04:02:38 ubuntu2204 npm[42441]: ???
??????
???
????????
???
????????????????
???
Jul 31 04:02:38 ubuntu2204 npm[42441]: ???
??????
???
????????
???
????????????????
???
Jul 31 04:02:38 ubuntu2204 npm[42441]: ????????????????
???
????????
???????????
??????
???
???
Jul 31 04:02:38 ubuntu2204 npm[42441]: ????????????????
???
????????
??????????
??????
???
???
Jul 31 04:02:38 ubuntu2204 npm[42441]: Release 0.4.8
В дереве процессов видна красивая иерархия - сначала запускается npm, тот вызывает оболочку с запуском временного скрипта, а уже последний поднимает основной процесс letschat. Память около 158 МБ - типовой расход для пустой инсталляции на Node.js, цифра вырастет по мере подключения пользователей и накопления истории.
Проверяем, что служба слушает свой порт:
ss -antpl | grep 5000
Ожидаемый вывод:
LISTEN 0
511
127.0.0.1:5000
0.0.0.0:*
users:(("letschat",pid=42441,fd=20))
Адрес 127.0.0.1:5000 показывает, что сервис слушает только локальный интерфейс на пятитысячном порту. Это нормально - снаружи к нему будет ходить Nginx, который мы поставим следующим шагом, а напрямую открывать Node-приложение в интернет не нужно.
Настройка Nginx как обратного прокси с поддержкой WebSocket для веб-интерфейса
Заставлять пользователей помнить порт 5000 и набирать его в браузере - не очень дружелюбно. Гораздо удобнее открыть Let's Chat на стандартном 80-м порту, что в перспективе откроет дорогу и к 443 с TLS-сертификатом. Для этой цели идеально подходит Nginx.
Ставим веб-сервер:
apt install nginx -y
Создаём отдельный файл конфигурации для нашего сайта:
nano /etc/nginx/conf.d/letschat.conf
Содержимое:
server {
server_name letschat.example.com;
listen 80;
access_log /var/log/nginx/lets_chat-access.log;
error_log /var/log/nginx/lets_chat-error.log;
location / {
proxy_set_header
X-Real-IP $remote_addr;
proxy_set_header
Host
$host;
proxy_http_version 1.1;
proxy_set_header
Upgrade $http_upgrade;
proxy_set_header
Connection 'upgrade';
proxy_cache_bypass $http_upgrade;
proxy_pass
http://127.0.0.1:5000;
}
}
Эта конфигурация заслуживает разбора. Параметр server_name стоит подменить на реальное доменное имя - именно по нему Nginx будет матчить входящие запросы. Опция listen 80 принимает обычные HTTP-соединения. Отдельные access_log и error_log для нашего сайта помогают при анализе трафика - проще искать проблемы в специфичных файлах, чем рыться в общем логе со всеми сайтами сервера.
Блок location / ловит все запросы и проксирует их в локальный Node-сервер на 127.0.0.1:5000. Заголовок X-Real-IP передаёт исходный IP клиента в Let's Chat - без него все запросы выглядели бы как идущие от localhost, что ломает аудит и любые проверки по IP. Заголовок Host передаёт имя домена, по которому пришёл запрос.
Теперь главные строки для работы мессенджера. Параметр proxy_http_version 1.1 включает использование HTTP/1.1 в общении с бэкендом - это нужно для поддержки keep-alive соединений и WebSocket. Заголовки Upgrade и Connection 'upgrade' выполняют тот самый протокольный апгрейд с обычного HTTP на WebSocket, через который Let's Chat доставляет сообщения в реальном времени. Без них чат будет показывать новые сообщения только после ручного обновления страницы.
Параметр proxy_cache_bypass $http_upgrade отключает кеширование для WebSocket-запросов. Кеширование стримингового двунаправленного соединения - идея настолько же странная, насколько и опасная.
Сохраняем файл и проверяем синтаксис:
nginx -t
Ожидаемый вывод:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Перезапускаем Nginx:
systemctl restart nginx
Проверяем статус:
systemctl status nginx
Ожидаемый вывод:
? nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2022-07-31 04:04:23 UTC; 5s ago
Docs: man:nginx(8)
Process: 43193 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 43194 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 43195 (nginx)
Tasks: 2 (limit: 2242)
Memory: 2.5M
CPU: 35ms
CGroup: /system.slice/nginx.service
??43195 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
??43196 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
Jul 31 04:04:23 ubuntu2204 systemd[1]: Starting A high performance web server and a reverse proxy server...
Jul 31 04:04:23 ubuntu2204 systemd[1]: Started A high performance web server and a reverse proxy server.
Память 2.5 МБ - типовой скромный расход Nginx, который славится своей экономностью. На один воркер-процесс приходится несколько мегабайт, и даже под огромной нагрузкой потребление редко выходит за десятки мегабайт.
Регистрация первой учётной записи и вход в веб-интерфейс мессенджера
Открываем браузер и переходим по адресу http://letschat.example.com (заменив домен на свой реальный). Появится страница входа с двумя полями для имени пользователя и пароля.
Поскольку это первый запуск, в системе ещё нет ни одного пользователя. Кликаем по кнопке "I need an account" для регистрации. Откроется форма создания аккаунта с полями для имени пользователя, пароля, email-адреса.
Заполняем данные и нажимаем кнопку Register. После успешной регистрации система покажет подтверждающий экран - кликаем OK для перехода обратно к форме входа. Вводим только что заданные имя пользователя и пароль, нажимаем Sign In. Открывается основной интерфейс Let's Chat с пустым списком комнат.
Дальше начинается обычная работа с мессенджером. Можно создавать новые комнаты для разных тем, приглашать в них участников, обмениваться сообщениями, использовать упоминания через @username, прикреплять ссылки и базовые мультимедиа-объекты. Все настройки уведомлений, тем оформления и личного профиля доступны из правого верхнего угла интерфейса.
Распространённые подводные камни и практические советы для эксплуатации в боевых условиях
В копилку наблюдений из практики стоит добавить несколько моментов, на которых обжигаются те, кто впервые поднимает Let's Chat в реальной работе.
Первая частая проблема - запуск от root. В дефолтном юнит-файле служба работает от суперпользователя, что создаёт серьёзный риск. Если в коде Let's Chat или в его зависимостях обнаружится уязвимость, злоумышленник получит полные права на систему. Лекарство - создать отдельного системного пользователя через useradd --system letschat, переместить установку в /opt/lets-chat с правильным владельцем и поправить юнит-файл. Это базовая гигиена, которой стоит следовать всегда.
Вторая популярная грабля - отсутствие HTTPS. По дефолту мы настроили обычный HTTP, и пароли при входе летят по сети открытым текстом. Любой сниффер на пути от клиента до сервера видит логин и пароль. Решение - подключить TLS через Let's Encrypt и certbot, что переводит весь трафик на 443-й порт с шифрованием. Для WebSocket это особенно важно, поскольку постоянное соединение без шифрования - идеальная мишень для перехвата.
Третий момент касается бэкапов. MongoDB бэкапится через утилиту mongodump, и эту команду стоит поставить на регулярный cron хотя бы раз в день. Без бэкапа потеря базы означает потерю всей истории переписки команды, что в зрелых проектах превращается в катастрофу - вместе с обсуждениями уходят документированные решения, ссылки, важные файлы.
Четвёртая тонкость - проект давно не развивается активно. Последняя версия Let's Chat (0.4.8) выпущена несколько лет назад, и обновлений ждать не стоит. Это означает накопленные уязвимости в зависимостях Node.js и MongoDB. Для серьёзной эксплуатации имеет смысл рассмотреть более активные альтернативы - Mattermost, Rocket.Chat, Zulip - которые продолжают развиваться и получать патчи безопасности. Let's Chat хорош для тестов, для маленьких внутренних команд без жёстких требований к безопасности или для случаев, когда простота важнее свежести.
Пятый момент - интеграция с LDAP. Если в компании уже есть Active Directory или OpenLDAP, имеет смысл подключить Let's Chat к существующему каталогу пользователей вместо локальной регистрации. Это избавляет от дублирования учётных записей и даёт автоматическое отключение доступа уволенным сотрудникам через единое управление в LDAP. Соответствующие настройки находятся в settings.yml и описаны в официальной документации проекта.
Где такая инсталляция пригодится в реальной жизни? Сценариев несколько. Тестовая среда для прототипирования корпоративной коммуникации перед выбором серьёзного решения. Внутренний чат для небольшой технической команды, где простота важнее обилия возможностей. Учебный проект для понимания архитектуры мессенджеров на Node.js и MongoDB. Лабораторные стенды для изучения интеграций с LDAP и XMPP без поднятия тяжёлых платформ.
Освоение Let's Chat даёт инженеру не просто навык установки одного приложения, а целый набор практик работы с Node.js-сервисами. Связка systemd-юнита, MongoDB как хранилища, Nginx с WebSocket-проксированием - это базовый стек для развёртывания сотен других подобных проектов. Тот, кто разобрался с Let's Chat, без труда поднимет Rocket.Chat, NodeBB, Ghost, Strapi и многие другие веб-приложения на Node. Принципы у них одинаковые - отличия только в специфике каждого продукта. И именно поэтому даже простой пробный сервер стоит того, чтобы развернуть его своими руками - это ступенька к более серьёзным самохостимым решениям, которые в перспективе помогают вернуть данные команды под собственный контроль.