За два года на фрилансе я задеплоил на VPS около полутора десятков ботов на aiogram и python-telegram-bot - от простых уведомлятелей для интернет-магазинов на WooCommerce до воронок с оплатой через Т‑Банк и трекингом доставки СДЭК. Деплой бота на VPS отличается от запуска на ноутбуке двумя вещами: сервер должен работать круглосуточно без вашего участия, а процесс - не падать намертво после разрыва SSH-сессии или перезагрузки машины. Ниже - три рабочих способа, которые я использую на практике: systemd для простых ботов, Docker для проектов с зависимостями и настройка webhook вместо long polling, когда бот обслуживает больше пары сотен пользователей в сутки.
Где размещать бота: VPS, PaaS или serverless
Перед тем как выбирать между systemd и Docker, нужно определиться с хостингом. Я перепробовал три варианта на разных проектах и для продакшн-бота почти всегда возвращаюсь к VPS.
| Вариант | Цена в месяц | Когда подходит | Главный минус |
|---|---|---|---|
| VPS (Timeweb, Selectel, Reg.ru) | от 200-400 ₽ | любые боты, фоновые задачи, парсеры | настройка вручную |
| PaaS (Railway, Render) | от 5-7 $ | быстрый MVP, тестовый прототип | проблемы с оплатой из РФ, риск блокировки |
| Serverless (Vercel/Cloudflare Functions) | по трафику | только webhook-боты без фоновых задач | холодный старт, лимит времени выполнения функции |
| Shared-хостинг | от 150 ₽ | почти никогда | нет root-доступа, процесс не держится в фоне |
VPS за 250-400 ₽ в месяц с 1 ГБ RAM спокойно тянет 3-5 ботов среднего размера, если они не гоняют тяжёлую обработку файлов или видео. Я обычно беру машину с 2 ГБ RAM и 1 vCPU - с запасом под Docker и логи.
Подготовка VPS перед деплоем бота
Первым делом - не работать под root. Создаю отдельного пользователя, обновляю систему и ставлю базовый набор пакетов:
adduser botuser
usermod -aG sudo botuser
apt update && apt upgrade -y
apt install python3-venv python3-pip ufw nginx -y
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable
Если бот будет работать через webhook - сразу открываю порты 80 и 443 и ставлю nginx, он понадобится под reverse proxy и SSL. Для polling-бота этого не нужно, он сам стучится к Telegram API исходящими запросами.
Ещё один момент, который часто упускают: часовой пояс сервера. Если бот работает с расписанием (например, шлёт напоминания в 9 утра по Москве), выставляю ‘timedatectl set-timezone Europe/Moscow‘ сразу, иначе потом ловишь баг с временем на проде и не сразу понимаешь причину.
Бесплатный материал
🎁 Полезный скрипт в подарок
Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.
Без спама. Отписка в 1 клик.
Systemd: запускаем бота как системный сервис
Для большинства простых ботов systemd - самый быстрый вариант деплоя. Не нужен Docker, процесс перезапускается автоматически при падении и при перезагрузке сервера.
Создаю виртуальное окружение и ставлю зависимости:
cd /home/botuser/mybot
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Дальше пишу unit-файл в ‘/etc/systemd/system/mybot.service‘:
[Unit]
Description=Telegram bot mybot
After=network.target
[Service]
Type=simple
User=botuser
WorkingDirectory=/home/botuser/mybot
ExecStart=/home/botuser/mybot/venv/bin/python bot.py
Restart=on-failure
RestartSec=5
EnvironmentFile=/home/botuser/mybot/.env
[Install]
WantedBy=multi-user.target
‘Restart=on-failure‘ и ‘RestartSec=5‘ - обязательные строки. Без них бот, упавший от необработанного исключения, просто останется лежать до ручного перезапуска. Включаю и стартую сервис:
sudo systemctl daemon-reload
sudo systemctl enable mybot
sudo systemctl start mybot
sudo journalctl -u mybot -f
Последняя команда - живой лог, в нём сразу видно, если бот не смог законнектиться к API или упал на старте из-за опечатки в токене. У меня в библиотеке готовых скриптов лежат шаблоны systemd-юнитов и docker-compose файлов под aiogram-ботов - беру их за основу на каждом новом проекте вместо того, чтобы писать заново.
Docker: деплой бота в контейнере
Docker беру, когда у бота есть зависимости помимо Python - например, Redis для FSM-состояний aiogram, Postgres под базу клиентов или ffmpeg для обработки голосовых. Изолировать это через systemd неудобно, а через контейнер - 10 минут работы.
Dockerfile:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "bot.py"]
docker-compose.yml:
version: "3.9"
services:
bot:
build: .
restart: unless-stopped
env_file: .env
volumes:
- ./data:/app/data
redis:
image: redis:7-alpine
restart: unless-stopped
‘restart: unless-stopped‘ в Docker делает то же, что ‘Restart=on-failure‘ в systemd - поднимает контейнер после падения или ребута хоста. Запуск и обновление:
docker compose up -d --build
docker compose logs -f bot
docker compose restart bot
На практике разница простая: systemd быстрее развернуть и легче дебажить логи через journalctl, Docker удобнее, если завтра нужно перенести бота на другой VPS или добавить второй сервис вроде очереди задач. Для одного простого бота без внешних зависимостей беру systemd, для проекта с базой и воркерами - Docker.
Webhook или long polling: что ставить на проде
По умолчанию aiogram и python-telegram-bot работают через long polling - бот сам раз в секунду опрашивает Telegram API на новые сообщения. Это просто, но на нагрузке от нескольких сотен активных пользователей в сутки начинает жрать CPU и добавляет задержку в ответах на 1-2 секунды.
Webhook переворачивает схему: Telegram сам присылает апдейты на ваш HTTPS-эндпоинт, как только что-то происходит. Для этого нужен домен или поддомен, SSL-сертификат и nginx в роли reverse proxy перед приложением на порту 8080:
server {
listen 443 ssl;
server_name bot.example.ru;
ssl_certificate /etc/letsencrypt/live/bot.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bot.example.ru/privkey.pem;
location /webhook {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
Сертификат получаю через ‘certbot - nginx ‑d bot.example.ru‘, обновляется он у certbot автоматически по крону. Код бота под webhook на aiogram 3.x:
from aiogram import Bot, Dispatcher
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from aiohttp import web
WEBHOOK_PATH = "/webhook"
WEBHOOK_URL = "https://bot.example.ru" + WEBHOOK_PATH
async def on_startup(bot: Bot):
await bot.set_webhook(WEBHOOK_URL)
app = web.Application()
SimpleRequestHandler(dispatcher=dp, bot=bot).register(app, path=WEBHOOK_PATH)
setup_application(app, dp, bot=bot)
if __name__ == "__main__":
web.run_app(app, host="127.0.0.1", port=8080)
Правило, которым пользуюсь сам: до 100-150 активных пользователей в сутки polling не создаёт проблем, дальше беру webhook - заметно снижает нагрузку на CPU и убирает задержку в ответах. Та же логика с reverse proxy у меня отработана на n8n-автоматизациях, которые тоже принимают вебхуки от внешних сервисов - подход идентичный, меняется только приложение за nginx.
Логи, автоперезапуск и мониторинг бота на проде
Падение бота ночью, когда никто не смотрит в терминал - самый частый сценарий проблем. У меня был случай с ботом на aiogram, который раз в 20-30 часов падал по memory leak из-за неправильно закрытой aiohttp-сессии - без Restart-политики он бы просто лежал до утра.
Для systemd логи смотрю через ‘journalctl ‑u mybot - since “1 hour ago“ ‘, для Docker - ‘docker compose logs ‑f - tail=200 bot‘. Оба варианта пишут в системный журнал, ротация логов настроена по умолчанию, вручную чистить не нужно.
Поверх Restart-политики ставлю простой health-check: cron раз в 5 минут дёргает ‘systemctl is-active mybot‘ или ‘docker inspect‘ на статус контейнера, и если сервис не активен - шлёт сообщение в отдельный чат через тот же Bot API. Это дешевле и надёжнее, чем полноценный Uptime Kuma для одного-двух ботов, но если у клиента их десяток - уже есть смысл поднять отдельный дашборд мониторинга.
Автоматизация в мессенджере
Telegram-бот / Mini App
от 30 000 ₽
Подробнее →Частые вопросы
Сколько стоит VPS для телеграм-бота?
На рынке хостинг-провайдеров минимальная конфигурация под 1-2 бота обходится в 150-400 ₽ в месяц (1 ГБ RAM, 1 vCPU). Для проекта с Docker, базой данных и несколькими ботами беру машину на 2-4 ГБ RAM, это уже 500-900 ₽ в месяц у большинства российских провайдеров.
Что лучше - systemd или Docker для деплоя бота?
Для одного бота без внешних зависимостей (без базы, без Redis) беру systemd - проще настроить и меньше слоёв абстракции при отладке. Если у проекта есть база данных, очередь задач или несколько сервисов, которые должны подниматься вместе, Docker Compose удобнее - весь стек описан в одном файле и переносится на другой сервер одной командой.
Нужен ли webhook, если у бота 50-100 пользователей?
Нет смысла усложнять деплой ради webhook на такой нагрузке. Long polling с systemd или Docker отработает стабильно, задержка в ответах будет незаметна пользователям. Webhook имеет смысл добавлять, когда бот переходит рубеж в несколько сотен активных пользователей в сутки или когда важна минимальная задержка (например, бот привязан к оплате и должен мгновенно подтверждать транзакцию).
Как обновить бота на проде без даунтайма?
Для systemd: ‘git pull‘, затем ‘sudo systemctl restart mybot‘ - перерыв в работе секунда-две. Для Docker: ‘git pull && docker compose up ‑d - build‘ - контейнер пересобирается и перезапускается автоматически. Если нужен апдейт совсем без разрыва (критично для ботов с оплатой), поднимаю вторую копию на другом порту, переключаю nginx на неё и только потом гашу старую - но для 95% ботов такой уровень избыточен.