Установка на свой сервер
Одна команда в терминале: установщик ставит Docker, забирает бандл, настраивает nginx, выписывает сертификат и поднимает продукт.
Ничего не собирается на месте и ничего не скачивается из интернета, кроме самого бандла. Всё, что нужно, — Debian, два домена и полчаса, большую часть которых занимает загрузка. Токен лицензии получается до установки: как его получить.
Что потребуется
| Что | Минимум | Рекомендуется |
|---|---|---|
| Система | Debian 13, amd64 | Debian 13, amd64 |
| Выделенных ядер | 2 | 4 |
| Общих (burstable) ядер вместо них | 4 | 6–8 |
| Память | 4 ГБ | 8 ГБ |
| Подкачка | 2 ГБ, обязательно | 2 ГБ |
| Диск | 40 ГБ SSD | 100 ГБ SSD |
| Операторов | до 5 | до 25 |
Только Debian 13 или новее. Ubuntu и другие дистрибутивы не поддерживаются, и это осознанное сужение: одна проверенная система вместо зоопарка, чтобы инструкция и наши обновления не расходились с тем, что у вас. Архитектура amd64; сборка под arm делается по отдельному запросу.
Про общие ядра. Цифры сняты с сервера, где ядра выделенные, со стопроцентной резервацией. На обычном VPS ядра общие и переподписанные: заявленные «4 ядра» на деле оказываются заметно меньше, особенно в часы нагрузки. Поэтому для общих ядер берите примерно вдвое больше номинала — это компенсация переподписки, а не запас на рост.
Про steal time. Это самая неочевидная причина медленной работы, и стоит разобраться с ней до покупки сервера. На переподписанном хостинге процессор формально есть, но его отбирает гипервизор. Если steal time устойчиво выше 5 процентов, пострадают в первую очередь фоновые задачи и обработка картинок: они станут заметно медленнее, хотя интерфейс будет выглядеть живым. Отзывчивость на сильно переподписанном хостинге мы гарантировать не можем. Установщик измеряет steal time первым шагом и говорит, что увидел; измерить самому можно в любой момент командой vmstat 1 10 — столбец st и есть steal time в процентах.
Про диск. Считать надо только вложения. База занимает около 1.6 КБ на сообщение: даже сто тысяч сообщений в месяц дают меньше 2 ГБ в год. Вложения на живом сервере занимают в полсотни раз больше базы, и растут они от того, что присылают ваши клиенты. Два рычага есть в продукте: срок хранения вложений и выгрузка в S3. При большом потоке картинок S3 дешевле, чем покупка диска.
Сборка на вашем сервере не нужна. Мы поставляем готовые образы, и это заметно снижает требования: сборка фронтенда сама по себе просит около 8 ГБ памяти, и если бы она шла на месте, минимальный профиль был бы вдвое выше.
Что подготовить до команды
- Два домена, оба A-записями на этот сервер: основной для интерфейса (
support.example.com) и отдельный для API и вебхуков (api.support.example.com). Записи должны уже разойтись по сети — сертификат выписывается проверкой домена, и на неразошедшемся DNS она не проходит. - Токен лицензии — строка, которая начинается на
SHLIC1.. Мы присылаем её при продаже. В ней же зашит адрес, откуда установщик заберёт бандл, поэтому больше ничего указывать не нужно. - Root на сервере. Установщик ставит пакеты и пишет в
/opt,/etc/supporthubи/etc/nginx.
Установка одной командой
curl -fsSL https://get.supporthub.cc/install | bash -s -- \
--key SHLIC1.… \
--domain support.example.com \
--api-domain api.support.example.com \
--email admin@example.com| Аргумент | Зачем |
|---|---|
| --key | токен лицензии. Обязателен: из него берётся адрес, откуда качается бандл |
| --domain | адрес интерфейса |
| --api-domain | адрес API и вебхуков |
| адрес для certbot: туда придут уведомления об истечении сертификата | |
| --bundle <файл|url> | взять бандл отсюда, а не качать у нас. Для закрытого контура |
| --no-tls | не трогать nginx и certbot: у вас свой прокси или балансировщик |
| --app-dir | куда ставить. По умолчанию /opt/supporthub |
| --dry-run | напечатать план и ничего не менять |
Что происходит по шагам — их видно в выводе:
- Сервер. Система, архитектура, память, подкачка, диск, steal time, выход к нашему серверу. Блокеры останавливают установку, замечания печатаются и не мешают.
- Docker. Если его нет, ставится из репозитория Docker для Debian, вместе с
docker-compose-plugin. - Бандл. Качается с адреса, зашитого в токен, в
/var/tmp. Обрыв не страшен: повторный запуск продолжает с места обрыва. - Проверка бандла. Контрольные суммы и, если мы передали вам открытый ключ, подпись. Дальше загружаются образы и раскладываются файлы.
- Настройки.
/opt/supporthub/.envзаполняется сам: пароль базы и ключ подписи сессий генерируются на месте, адреса берутся из аргументов, число рабочих процессов подбирается по объёму памяти. - Агент обслуживания. На хост ставится служба
supporthub-agent— через неё работают кнопка обновления и доступ поддержке в разделе «Лицензия». Вместе с ней появляется командаsupporthubдля тех же действий из терминала. - Лицензия. Токен кладётся в
/etc/supporthub/license.key— файлом, а не переменной среды: так он не попадает ни вdocker inspect, ни в историю оболочки. - nginx и сертификат. Два server-блока и certbot. Если nginx на этом сервере уже настроен на ваши домены, шаг пропускается целиком — свой конфиг мы не перезаписываем.
- Код установки. Установщик выпускает его сам и печатает в конце.
- Запуск.
docker compose up -dи ожидание, пока бэкенд ответит на/api/health.
Посмотреть, что будет сделано, ничего не меняя:
curl -fsSL https://get.supporthub.cc/install | bash -s -- --key SHLIC1.… \
--domain support.example.com --api-domain api.support.example.com \
--email admin@example.com --dry-runПроверить подпись бандла. Если мы передали вам открытый ключ релизов, положите его на сервер и укажите путь переменной — тогда подпись не просто отметится, а проверится:
SH_RELEASE_PUBKEY=/root/supporthub-release.pem curl -fsSL https://get.supporthub.cc/install | bash -s -- --key …Закрытый контур. Если сервер не ходит в интернет, попросите у нас бандл файлом и передайте его установщику через --bundle: сам он тогда ничего не скачивает. Скрипт установщика в этом случае тоже надо принести с собой — он лежит в бандле под именем bootstrap.bash:
tar -xzf supporthub-dedicated-2026.09.30.tar.gz bootstrap.bash
bash bootstrap.bash --key SHLIC1.… --domain support.example.com \
--api-domain api.support.example.com --email admin@example.com \
--bundle /root/supporthub-dedicated-2026.09.30.tar.gzСертификат в закрытом контуре certbot не выпишет: ставьте с --no-tls и настраивайте свой прокси, либо кладите сертификат сами.
Первое открытие страницы
В конце работы установщик печатает адрес и код установки — двенадцать знаков вида ABCD-EFGH-JKLM. Откройте адрес в браузере: мастер первого запуска спросит код, а затем завёдет владельца и первый проект.
Код нужен потому, что свежий сервер стоит в интернете раньше, чем к нему подойдёт хозяин: без кода владельцем стал бы тот, кто первым открыл страницу. Он лежит в /etc/supporthub/setup-code и спрашивается один раз — после того как владелец завёлся, мастер закрывается совсем и страница /setup перестаёт существовать.
О лицензии мастер не спросит: токен уже положил установщик. Поле для ключа в мастере есть на случай, когда установка шла без токена, и введённый там ключ берёт верх над файлом.
Дальше всё внутри продукта: приглашения операторов, подключение каналов, настройки. Единственное, что стоит дописать в /opt/supporthub/.env руками, — свой SMTP (SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM) и перезапустить docker compose up -d: без него не уходят приглашения операторов и уведомления. Установщик про это тоже напомнит.
Активация лицензии
Установке нужен хотя бы один выход к нашему серверу лицензий. Это не постоянное соединение: проверка идёт при старте и дальше раз в шесть часов, наружу уходят идентификатор лицензии, отпечаток сервера, версия сборки, число активных операторов и отметка времени. Содержимое обращений, сообщения и персональные данные не передаются и нам недоступны.
Свежая установка работает без активации не дольше семи дней. За это время нужна хотя бы одна успешная проверка. После неё установка переживает отсутствие связи столько дней, сколько задано в лицензии: наша авария не должна становиться вашей. Первая проверка обычно проходит сама, пока вы заполняете мастер, — состояние лицензии видно в разделе «Лицензия».
Если сервер в закрытом контуре, наружу он не ходит вовсе. Скажите нам об этом заранее: вы присылаете отпечаток сервера, мы выпускаем квитанцию офлайн, вы кладёте её файлом. Отпечаток показывается в разделе «Лицензия» и его же печатает команда:
docker compose exec backend python -c "from app.licensing import fingerprint; print(fingerprint.current())"Профили: бот и WhatsApp
По умолчанию поднимается только то, что нужно всем. Необязательные части включаются профилями:
cd /opt/supporthub
docker compose --profile bot up -d # свой телеграм-бот
docker compose --profile whatsapp up -d # шлюз WhatsAppБез WhatsApp — минус контейнер и минус его память. Включать стоит только то, чем пользуетесь.
Резервные копии
Ответственность ваша, но рецепт готовый. Нужны две вещи: база и каталог вложений.
#!/bin/bash
set -euo pipefail
DEST=/var/backups/supporthub
DAY=$(date +%F)
mkdir -p "$DEST"
cd /opt/supporthub
# База. Через pg_dump внутри контейнера — снимок согласованный,
# останавливать ничего не нужно.
docker compose exec -T db pg_dump -U postgres supporthub | gzip -6 > "$DEST/db-$DAY.sql.gz"
# Вложения. Это том docker, поэтому копируем изнутри контейнера.
docker compose exec -T backend tar -cf - -C /app media | gzip -6 > "$DEST/media-$DAY.tar.gz"
# Настройки, лицензия и код установки — маленькие, но без них
# восстановление неполное.
tar -czf "$DEST/conf-$DAY.tar.gz" /opt/supporthub/.env /etc/supporthub
find "$DEST" -name '*.gz' -mtime +14 -deletechmod +x /usr/local/bin/supporthub-backup
echo '15 3 * * * /usr/local/bin/supporthub-backup' | crontab -Копия на том же диске спасает от ошибки, но не от потери сервера: увозите её куда-нибудь ещё.
Восстановление:
cd /opt/supporthub
docker compose down
docker compose up -d db
gunzip -c /var/backups/supporthub/db-2026-09-30.sql.gz | docker compose exec -T db psql -U postgres supporthub
docker compose up -dОбновления
Обновление — та же команда установки с новым бандлом. Установщик запоминает предыдущий тег образов, так что откат занимает минуту:
/opt/supporthub/rollback.bashПеред переключением версии снимается копия базы — она и делает откат безусловным: образы откатываются за минуту, схема базы так не откатывается. Прежние образы с сервера не удаляются, иначе откатываться было бы некуда. Если место поджимает, удаляйте вручную и только те теги, которые точно не нужны.
Доступ для обновлений. В настройках есть переключатель, которым вы разрешаете или запрещаете нам заходить на сервер для установки обновлений. Пока он выключен, мы на сервер не идём — вообще, а не «идём, но ничего не меняем». Обновления в этом случае передаются вам бандлом, и устанавливаете их вы сами. Отзыв доступа не влияет ни на работу продукта, ни на срок лицензии. Подробно — в обслуживании установки.
Если ставить по шагам
Установщик делает всё то же, что описано ниже, и этот раздел нужен в двух случаях: когда на сервере уже живёт что-то своё и хочется контролировать каждый шаг, или когда надо понять, что именно произошло. Если вы ставили одной командой — этот раздел можно не читать.
1. Проверить сервер
./preinstall-check.bashСкрипт лежит в бандле, ничего не меняет и только смотрит: систему, архитектуру, память, подкачку, диск, docker, выход наружу, nginx и steal time. Те же проверки установщик делает первым шагом.
2. Установить Docker
apt install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list
apt update
apt install docker-ce docker-ce-cli containerd.io docker-compose-pluginНужен именно docker-compose-plugin (команда docker compose), а не старый docker-compose.
3. Домен и сертификат
nginx работает на хосте, а не в контейнере. Так удобнее вам: certbot рядом, конфиг там, где вы привыкли, и обновления продукта не задевают ваш TLS. Контейнеры слушают только loopback (127.0.0.1:3000 и 127.0.0.1:8000), наружу их выпускает ваш nginx.
Блоки пишутся на 80 порт, а 443, сертификат и перенаправление дописывает certbot. Наоборот не выйдет: конфиг с ssl_certificate до выписки сертификата уложит nginx на первом же reload.
server {
listen 80;
server_name support.example.com;
client_max_body_size 50m; # вложения
location / {
proxy_pass http://127.0.0.1:3000;
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 api.support.example.com;
client_max_body_size 50m;
location / {
proxy_pass http://127.0.0.1:8000;
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;
# Живые обновления в интерфейсе оператора идут по WebSocket.
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
}apt install certbot python3-certbot-nginx
certbot --nginx -d support.example.com -d api.support.example.com4. Развернуть бандл
mkdir -p /root/supporthub-bundle && cd /root/supporthub-bundle
tar -xzf /путь/к/supporthub-dedicated-*.tar.gz
./install.bash --check-only # проверить целостность, ничего не менять
./install.bash # установить--check-only сверяет контрольные суммы и печатает состав бандла. Если мы передали вам открытый ключ подписи, проверить и её:
./install.bash --pubkey /путь/к/supporthub-release.pem --check-onlyПервый запуск разворачивает файлы в /opt/supporthub, загружает образы и останавливается: дальше нужно заполнить настройки.
5. Заполнить настройки
nano /opt/supporthub/.envОбязательные поля помечены в файле. Коротко:
DB_PASSWORD— пароль базы, придумать один раз. Он попадает в строку подключения шлюза WhatsApp, поэтому только буквы и цифры:openssl rand -hex 24;SECRET_KEY—openssl rand -base64 48; смена разлогинит всех операторов;FRONTEND_URL,BACKEND_PUBLIC_URL,BACKEND_PUBLIC_WS,WEBHOOK_BASE_URL— ваши адреса из шага 3;WEBAUTHN_RP_ID,WEBAUTHN_ORIGIN— для входа по устройству;SMTP_*— ваш SMTP. Без него не уходят приглашения операторов и уведомления.
На минимальном профиле (2 ядра, 4 ГБ) поставьте:
SH_WEB_CONCURRENCY=1
SH_CELERY_CONCURRENCY=2Иначе два процесса бэкенда вместе с Celery упираются в память, и всплеск при обработке вложений заканчивается убитым воркером.
Чтобы раздел «Система» в админке видел контейнеры, подставьте номер группы docker:
getent group docker | cut -d: -f36. Положить токен лицензии
install -d -m 750 -g 1000 /etc/supporthub
install -m 640 -g 1000 /путь/к/license.key /etc/supporthub/license.keyТокен лежит файлом, а не переменной среды: так он не попадает ни в docker inspect, ни в историю оболочки. Группа 1000 — та, под которой работает бэкенд в контейнере; с правами root:root файл внутри не читается, и установка молча уходит в режим только чтения.
Без токена установка поднимется, но уйдёт в режим только чтения: входящие обращения будут приниматься и сохраняться, а отвечать на них не получится.
7. Выпустить код установки
Без кода мастер первого запуска не пустит никого — и это правильно: код закрывает окно между «сервер появился в интернете» и «у установки появился хозяин». Придумайте строку (годится и случайная) и положите её рядом с лицензией:
openssl rand -hex 6 | install -m 640 -g 1000 /dev/stdin /etc/supporthub/setup-code
cat /etc/supporthub/setup-codeРегистр, дефисы и пробелы при сверке не значат ничего: код набирают руками.
8. Запустить и открыть страницу
cd /opt/supporthub
docker compose up -d
docker compose psДальше — как при установке одной командой: откройте свой адрес, мастер спросит код установки и завёдет владельца.
Если что-то не работает
- 1Установщик остановился на проверке сервераОн печатает, какая именно проверка не прошла. Блокеры — это система не Debian 13, не amd64, меньше 4 ГБ памяти, меньше 40 ГБ диска, отсутствующая подкачка на 4 ГБ памяти и steal time выше 5 процентов. Всё это лечится на стороне хостинга, и лечить это надо до установки.
- 2Бандл не скачалсяПроверьте, что сервер видит интернет и что токен лицензии действующий. Если выхода наружу нет, попросите бандл файлом и поставьте с
--bundle. - 3Бэкенд не поднялся, установщик показал журналЧаще всего это база, которая ещё не готова — compose ждёт её сам, но при медленном диске первый запуск занимает минуту-две, — или незаполненное поле в
.env. Настройки правятся в/opt/supporthub/.env, после правкиcd /opt/supporthub && docker compose up -d. Повторный запуск установщика настройки не перетирает. - 4certbot не выписал сертификатПочти всегда DNS: оба имени должны указывать на этот сервер, и записи должны разойтись по сети. Установщик в этом случае не останавливается — продукт поднимается и работает по http, — и печатает команду, которой выписать сертификат позже.
- 5Мастер первого запуска не открывается, страница уводит на входЗначит установка уже занята: владелец завёлся. Это не поломка, а защита — второй раз мастер не откроется.
- 6Мастер говорит, что код установки не настроенФайла
/etc/supporthub/setup-codeнет или он пустой. Положите код по образцу из шага 7 ручной установки и обновите страницу: файл читается при каждой проверке, перезапускать контейнеры не нужно. - 7Интерфейс открывается, но данные не грузятсяПочти всегда
BACKEND_PUBLIC_URLне совпадает с тем, что реально проксирует nginx. Адрес попадает в сборку фронтенда, поэтому его значение должно быть правильным до того, как мы собрали вам образ: если адрес поменялся, нужна новая сборка. - 8Интерфейс оператора замираетНе хватает настроек WebSocket в nginx:
proxy_http_version 1.1, заголовкиUpgrade/Connectionи большойproxy_read_timeout. - 9Вложения не отправляются
client_max_body_sizeв nginx. - 10Появился баннер про лицензиюСвязи с сервером лицензий нет. Проверить выход наружу; продукт продолжит работать столько дней, сколько задано в лицензии, и восстановится сам, как только связь вернётся.
- 11Всё медленно, но процессор «свободен»Steal time. Вернуться к разделу «Что потребуется».

