Установка на свой сервер
На своём сервере

Установка на свой сервер

Одна команда в терминале: установщик ставит Docker, забирает бандл, настраивает nginx, выписывает сертификат и поднимает продукт.

Ничего не собирается на месте и ничего не скачивается из интернета, кроме самого бандла. Всё, что нужно, — Debian, два домена и полчаса, большую часть которых занимает загрузка. Токен лицензии получается до установки: как его получить.

Что потребуется

ЧтоМинимумРекомендуется
СистемаDebian 13, amd64Debian 13, amd64
Выделенных ядер24
Общих (burstable) ядер вместо них46–8
Память4 ГБ8 ГБ
Подкачка2 ГБ, обязательно2 ГБ
Диск40 ГБ SSD100 ГБ 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 ГБ памяти, и если бы она шла на месте, минимальный профиль был бы вдвое выше.

Что подготовить до команды

  1. Два домена, оба A-записями на этот сервер: основной для интерфейса (support.example.com) и отдельный для API и вебхуков (api.support.example.com). Записи должны уже разойтись по сети — сертификат выписывается проверкой домена, и на неразошедшемся DNS она не проходит.
  2. Токен лицензии — строка, которая начинается на SHLIC1.. Мы присылаем её при продаже. В ней же зашит адрес, откуда установщик заберёт бандл, поэтому больше ничего указывать не нужно.
  3. 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 и вебхуков
--emailадрес для certbot: туда придут уведомления об истечении сертификата
--bundle <файл|url>взять бандл отсюда, а не качать у нас. Для закрытого контура
--no-tlsне трогать nginx и certbot: у вас свой прокси или балансировщик
--app-dirкуда ставить. По умолчанию /opt/supporthub
--dry-runнапечатать план и ничего не менять

Что происходит по шагам — их видно в выводе:

  1. Сервер. Система, архитектура, память, подкачка, диск, steal time, выход к нашему серверу. Блокеры останавливают установку, замечания печатаются и не мешают.
  2. Docker. Если его нет, ставится из репозитория Docker для Debian, вместе с docker-compose-plugin.
  3. Бандл. Качается с адреса, зашитого в токен, в /var/tmp. Обрыв не страшен: повторный запуск продолжает с места обрыва.
  4. Проверка бандла. Контрольные суммы и, если мы передали вам открытый ключ, подпись. Дальше загружаются образы и раскладываются файлы.
  5. Настройки. /opt/supporthub/.env заполняется сам: пароль базы и ключ подписи сессий генерируются на месте, адреса берутся из аргументов, число рабочих процессов подбирается по объёму памяти.
  6. Агент обслуживания. На хост ставится служба supporthub-agent — через неё работают кнопка обновления и доступ поддержке в разделе «Лицензия». Вместе с ней появляется команда supporthub для тех же действий из терминала.
  7. Лицензия. Токен кладётся в /etc/supporthub/license.key — файлом, а не переменной среды: так он не попадает ни в docker inspect, ни в историю оболочки.
  8. nginx и сертификат. Два server-блока и certbot. Если nginx на этом сервере уже настроен на ваши домены, шаг пропускается целиком — свой конфиг мы не перезаписываем.
  9. Код установки. Установщик выпускает его сам и печатает в конце.
  10. Запуск. 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 — минус контейнер и минус его память. Включать стоит только то, чем пользуетесь.

Резервные копии

Ответственность ваша, но рецепт готовый. Нужны две вещи: база и каталог вложений.

/usr/local/bin/supporthub-backupbash
#!/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 -delete
chmod +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.

nginx
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.com

4. Развернуть бандл

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: -f3

6. Положить токен лицензии

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. 1
    Установщик остановился на проверке сервера
    Он печатает, какая именно проверка не прошла. Блокеры — это система не Debian 13, не amd64, меньше 4 ГБ памяти, меньше 40 ГБ диска, отсутствующая подкачка на 4 ГБ памяти и steal time выше 5 процентов. Всё это лечится на стороне хостинга, и лечить это надо до установки.
  2. 2
    Бандл не скачался
    Проверьте, что сервер видит интернет и что токен лицензии действующий. Если выхода наружу нет, попросите бандл файлом и поставьте с --bundle.
  3. 3
    Бэкенд не поднялся, установщик показал журнал
    Чаще всего это база, которая ещё не готова — compose ждёт её сам, но при медленном диске первый запуск занимает минуту-две, — или незаполненное поле в .env. Настройки правятся в /opt/supporthub/.env, после правки cd /opt/supporthub && docker compose up -d. Повторный запуск установщика настройки не перетирает.
  4. 4
    certbot не выписал сертификат
    Почти всегда DNS: оба имени должны указывать на этот сервер, и записи должны разойтись по сети. Установщик в этом случае не останавливается — продукт поднимается и работает по http, — и печатает команду, которой выписать сертификат позже.
  5. 5
    Мастер первого запуска не открывается, страница уводит на вход
    Значит установка уже занята: владелец завёлся. Это не поломка, а защита — второй раз мастер не откроется.
  6. 6
    Мастер говорит, что код установки не настроен
    Файла /etc/supporthub/setup-code нет или он пустой. Положите код по образцу из шага 7 ручной установки и обновите страницу: файл читается при каждой проверке, перезапускать контейнеры не нужно.
  7. 7
    Интерфейс открывается, но данные не грузятся
    Почти всегда BACKEND_PUBLIC_URL не совпадает с тем, что реально проксирует nginx. Адрес попадает в сборку фронтенда, поэтому его значение должно быть правильным до того, как мы собрали вам образ: если адрес поменялся, нужна новая сборка.
  8. 8
    Интерфейс оператора замирает
    Не хватает настроек WebSocket в nginx: proxy_http_version 1.1, заголовки Upgrade/Connection и большой proxy_read_timeout.
  9. 9
    Вложения не отправляются
    client_max_body_size в nginx.
  10. 10
    Появился баннер про лицензию
    Связи с сервером лицензий нет. Проверить выход наружу; продукт продолжит работать столько дней, сколько задано в лицензии, и восстановится сам, как только связь вернётся.
  11. 11
    Всё медленно, но процессор «свободен»
    Steal time. Вернуться к разделу «Что потребуется».
Была ли страница полезной?