Хостинг
Бот — это постоянный процесс, который держит WebSocket-соединение с Gateway. Значит он должен где-то крутиться круглосуточно и автоматически подниматься после ребута / краша.
Выбор хостинга
Заголовок раздела «Выбор хостинга»| Вариант | Когда подходит | Цена |
|---|---|---|
| VPS (Timeweb, Selectel, Hetzner, DO) | Один-два бота, полный контроль | от 300 ₽/мес |
| Serverless (Cloudflare Workers, AWS Lambda) | Боты без gateway — только webhooks или REST-cron. Полноценный бот там не живёт — они убивают долгие соединения | $0-5/мес |
| PaaS (Railway, Fly.io, Render) | Быстрый старт, деплой из git | $5-10/мес |
| Своя инфра (Docker Swarm/K8s) | Несколько ботов, мониторинг, роллинг-апдейты | зависит |
Минимум: VPS + systemd
Заголовок раздела «Минимум: VPS + systemd»Самый надёжный дешёвый вариант. Арендуете VPS (1 CPU / 1 GB RAM хватит для любого одиночного бота), ставите Node и заводите systemd-юнит.
/opt/mybot/ ├── src/ ├── node_modules/ ├── data/ ← SQLite здесь ├── .env ← FLOODILKA_BOT_TOKEN └── package.json[Unit]Description=My Floodilka botAfter=network.target
[Service]Type=simpleUser=botuserWorkingDirectory=/opt/mybotEnvironmentFile=/opt/mybot/.envExecStart=/usr/bin/node /opt/mybot/src/index.jsRestart=alwaysRestartSec=5StandardOutput=append:/var/log/mybot.logStandardError=append:/var/log/mybot.log
# БезопасностьNoNewPrivileges=truePrivateTmp=trueProtectSystem=strictReadWritePaths=/opt/mybot/data /var/log/mybot.log
[Install]WantedBy=multi-user.target# Первый запускsudo systemctl daemon-reloadsudo systemctl enable --now mybot
# Смотреть логиsudo journalctl -u mybot -f
# Перезапустить после обновления кодаsudo systemctl restart mybot
# Остановитьsudo systemctl stop mybotОсновные моменты:
Restart=always+RestartSec=5— systemd поднимет бота после любого паденияNoNewPrivileges,PrivateTmp,ProtectSystem=strict— минимальная изоляция- Логи в
/var/log/mybot.log— можно крутитьlogrotate’ом
Если вы уже живёте в docker — упаковываете бот в образ и деплоите:
FROM node:24-slimWORKDIR /app
# Копируем только package.json и lockfile сначала — ради кешаCOPY package.json pnpm-lock.yaml* ./RUN corepack enable && pnpm install --prod --frozen-lockfile
COPY src ./srcVOLUME ["/app/data"]
USER nodeCMD ["node", "src/index.js"]services: bot: build: . restart: unless-stopped environment: FLOODILKA_BOT_TOKEN: ${FLOODILKA_BOT_TOKEN} volumes: - ./data:/app/data logging: driver: json-file options: max-size: 10m max-file: 5PaaS: Railway / Fly.io
Заголовок раздела «PaaS: Railway / Fly.io»Быстро и без серверной возни:
- Railway —
railway.app, подключаешь репозиторий, пушишь в main → деплой.FLOODILKA_BOT_TOKENв Variables. - Fly.io —
fly deployиз CLI.fly secrets set FLOODILKA_BOT_TOKEN=....
Оба умеют persistent-volumes для SQLite-файла.
Что точно должно быть независимо от выбора
Заголовок раздела «Что точно должно быть независимо от выбора»1. Авто-перезапуск
Заголовок раздела «1. Авто-перезапуск»Бот упадёт. WebSocket разорвётся. Процесс закрешится. Любой хостинг-выбор должен давать автоматический restart — systemd, Docker restart: always, Railway auto-restart.
2. Переменные окружения, не файлы
Заголовок раздела «2. Переменные окружения, не файлы»Не коммитьте .env в git. Токен живёт в secrets-управлении хостинга (systemd EnvironmentFile, Docker compose env, Railway Variables).
3. Persistent storage
Заголовок раздела «3. Persistent storage»Если бот пишет в БД — убедитесь что volume переживает рестарт контейнера. Типовой просчёт: Docker-контейнер без volume → SQLite-файл стирается при каждом docker-compose up.
4. Логи
Заголовок раздела «4. Логи»Минимум — пишите console.log со временем в stdout. Systemd/Docker сами сохранят. Для product-grade — см. Мониторинг.
5. Graceful shutdown
Заголовок раздела «5. Graceful shutdown»function shutdown() { console.log('shutting down...'); ws?.close(1000, 'shutdown'); db?.close(); process.exit(0);}process.on('SIGTERM', shutdown);process.on('SIGINT', shutdown);Без этого после systemctl stop процесс убьётся через KillMode=mixed и SQLite может остаться в неконсистентном состоянии.
Что дальше
Заголовок раздела «Что дальше»- Мониторинг — как понять что бот жив
- Примеры / Уровневый бот — полный пример с SQLite и systemd-юнитом