MAATRIX / Блог / Как поднять Telegram-бота на Python на VPS

Как поднять Telegram-бота на Python на VPS

Как поднять Telegram-бота на Python на VPS

MAATRIX

Бот, запущенный с ноутбука, живёт ровно до закрытия крышки: пропал Wi-Fi — пропал сервис. Telegram-бот на VPS снимает это за вечер: чистая Ubuntu, venv, aiogram 3 и служба systemd, которая поднимает процесс после падения и перезагрузки. Ниже — весь путь с командами, конфигами и текстами ошибок, на которых застревают.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что подготовить до подключения к серверу

Токен берётся у @BotFather: /newbot, имя, юзернейм с окончанием bot. В ответ приходит строка вида 8123456789:AAHk9m.... Проверьте её ещё до сервера командой curl -s "https://api.telegram.org/bot<ТОКЕН>/getMe": живой токен отвечает {"ok":true,"result":{"id":8123456789,"is_bot":true,"username":"my_test_bot"}}, а на {"ok":false,"error_code":401,"description":"Unauthorized"} дальше идти бессмысленно. Токен — полный доступ к боту: не коммитьте его в репозиторий, а если засветили — /revoke выдаст новый и убьёт старый.

Второе решение — библиотека. Выбор 2026 года: aiogram 3.x; альтернативы — python-telegram-bot 21–22 и pyTelegramBotAPI.

Третье — режим работы. При long polling бот сам ходит на api.telegram.org за апдейтами: входящие порты, домен и сертификат не нужны, при webhook нужно всё перечисленное. Начинайте с polling.

Базовая подготовка Ubuntu: пользователь, SSH, ufw

Берите Ubuntu 24.04 LTS: из коробки python3 версии 3.12.3 и pip 24.0, поддержка до 2029 года. Дальше apt update && apt upgrade -y, отдельный пользователь вместо root (adduser deploy, usermod -aG sudo deploy), вход по ключу через ssh-copy-id deploy@ВАШ_IP и три строки в /etc/ssh/sshd_config.d/99-hardening.conf: PasswordAuthentication no, PermitRootLogin prohibit-password, KbdInteractiveAuthentication no.

Грабля, специфичная для 24.04: sshd запускается через сокет-активацию, поэтому смена порта в sshd_config не даёт ничего — порт слушает ssh.socket. Уводить SSH с 22-го надо через systemctl edit ssh.socket, обнулив список пустой строкой ListenStream=. После правок — systemctl restart ssh и проверка входа вторым терминалом.

Фаервол для polling-бота скучный:

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw enable

ufw status покажет Status: active и единственное правило на 22/tcp: боту на long polling входящие соединения не нужны вообще. Добавьте apt install -y fail2ban unattended-upgrades — джейл sshd включён по умолчанию, а через сутки fail2ban-client status sshd покажет десятки забаненных адресов.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть бот-стек

Python, venv и грабля externally-managed-environment

Первый рефлекс на свежей 24.04 — pip3 install aiogram под root. Ответ системы:

error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you want to install.

Это не поломка, а PEP 668: системный Python защищён от пакетов с PyPI, потому что ими же чинится сам apt. Путь — venv, а не флаг --break-system-packages.

apt install -y python3-venv python3-pip git
useradd --system --home /opt/tgbot --shell /usr/sbin/nologin tgbot
mkdir -p /opt/tgbot/data && chown -R tgbot:tgbot /opt/tgbot
sudo -u tgbot python3 -m venv /opt/tgbot/venv

Системный пользователь без shell — вторая линия обороны: у процесса нет ни sudo, ни домашней директории, ни возможности залогиниться. Зависимости фиксируйте в /opt/tgbot/requirements.txt строкой aiogram>=3.15,<4: граница <4 важна, мажорный апдейт ломает API, и «упавший после деплоя бот» чаще всего про незакреплённую версию.

Ставим всё одной командой sudo -u tgbot /opt/tgbot/venv/bin/pip install -r /opt/tgbot/requirements.txt. Цифра для планирования диска: окружение с aiogram и зависимостями (aiohttp, pydantic, magic-filter, certifi) заняло у нас 104 МБ по du -sh /opt/tgbot/venv.

Минимальный бот на aiogram 3 и первый запуск руками

Файл /opt/tgbot/bot.py — рабочий минимум:

import asyncio, logging, os

from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
from aiogram.filters import CommandStart
from aiogram.types import Message

dp = Dispatcher()

@dp.message(CommandStart())
async def cmd_start(message: Message) -> None:
    await message.answer("Бот <b>на связи</b>.")

async def main() -> None:
    logging.basicConfig(level=logging.INFO)
    bot = Bot(
        token=os.environ["BOT_TOKEN"],
        default=DefaultBotProperties(parse_mode=ParseMode.HTML),
    )
    await dp.start_polling(bot)

if __name__ == "__main__":
    asyncio.run(main())

Обратите внимание на DefaultBotProperties: с aiogram 3.7 конструктор Bot() не принимает parse_mode= напрямую, и код из старых статей падает с TypeError ещё до подключения к Telegram — частая причина «скопировал пример, не работает».

Запуск руками, до systemd:

BOT_TOKEN='8123456789:AAHk9m...' sudo -u tgbot -E /opt/tgbot/venv/bin/python /opt/tgbot/bot.py

В логе появится INFO:aiogram.dispatcher:Start polling и юзернейм бота. Три ошибки этого шага:

  • TelegramUnauthorizedError: Telegram server says - Unauthorized — токен неверный, обрезан при копировании или отозван.
  • TelegramConflictError: Telegram server says - Conflict: terminated by other getUpdates request; make sure that only one bot instance is running — жива вторая копия, обычно забытая на ноутбуке. Одному токену — ровно один процесс с polling.
  • Conflict: can't use getUpdates method while webhook is active; use deleteWebhook to delete the webhook first — на токене висит вебхук с прошлых экспериментов: curl -s "https://api.telegram.org/bot<ТОКЕН>/deleteWebhook?drop_pending_updates=true".

Бот ответил — гасите его через Ctrl+C: screen и tmux для постоянной работы не годятся, сессия не переживёт перезагрузку.

systemd-служба: автозапуск, перезапуск и ограничение прав

Секреты — в отдельный файл /etc/tgbot/bot.env со строками BOT_TOKEN=... и ADMIN_ID=..., закрытый через chown root:tgbot и chmod 640. Злая деталь: в EnvironmentFile нельзя писать export BOT_TOKEN=... и не нужно кавычек — systemd примет export за часть имени переменной, а кавычки попадут внутрь токена, и вы получите Unauthorized при верном токене.

Юнит /etc/systemd/system/tgbot.service:

[Unit]
Description=Telegram bot (aiogram)
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0

[Service]
Type=simple
User=tgbot
Group=tgbot
WorkingDirectory=/opt/tgbot
EnvironmentFile=/etc/tgbot/bot.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/tgbot/venv/bin/python /opt/tgbot/bot.py
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/tgbot/data

[Install]
WantedBy=multi-user.target

Неочевидные строки:

  • StartLimitIntervalSec=0 снимает ограничитель перезапусков. Без него systemd после пяти рестартов за 10 секунд сдаётся: Start request repeated too quickly. и Failed with result 'start-limit-hit' — бот лежит, хотя Restart=always стоит. А RestartSec=5 даёт Telegram время «отпустить» прошлую сессию getUpdates.
  • PYTHONUNBUFFERED=1 — без него Python буферизует stdout при выводе не в терминал, и логи в journalctl приходят пачками с задержкой в минуты.
  • ProtectSystem=strict делает файловую систему read-only для процесса. Забыли ReadWritePaths при работе с SQLite — получите sqlite3.OperationalError: attempt to write a readonly database: выглядит как проблема прав на файл, а это ограничение systemd.
  • After=network-online.target без Wants= не работает: цель не активируется, бот стартует раньше сети.

Дальше systemctl daemon-reload и systemctl enable --now tgbot. Здоровый systemctl status tgbot — запомните строку Memory, к ней вернёмся при выборе тарифа:

● tgbot.service - Telegram bot (aiogram)
     Active: active (running) since Fri 2026-08-28 09:14:22 UTC; 3min 8s ago
   Main PID: 1482 (python)
      Tasks: 3 (limit: 1131)
     Memory: 68.4M (peak: 71.2M)

Логи в реальном времени — journalctl -u tgbot -f -o cat, где -o cat убирает служебные префиксы. Проверьте перезагрузкой: после reboot команда systemctl is-active tgbot должна ответить active, иначе причина найдётся в journalctl -u tgbot -b. И ограничьте журнал строкой SystemMaxUse=200M в /etc/systemd/journald.conf: по умолчанию journald берёт до 10% раздела.

Webhook, лимиты Telegram и обновление без простоя

Long polling по умолчанию держит соединение 30 секунд: около двух запросов в минуту в простое и меньше 100 МБ служебного трафика в месяц. Для большинства ботов это финальная конфигурация; webhook нужен, когда апдейты идут тысячами в минуту.

При переходе помните жёсткое ограничение: Telegram стучится только на порты 443, 80, 88 и 8443, никакие 5000 или 8080 наружу не примутся. Путь: домен → certbot --nginx -d bot.example.com → проксирование на локальный порт → ufw allow 443/tcp. И обязательно секрет, иначе эндпоинт сможет дёргать кто угодно:

curl -s -X POST "https://api.telegram.org/bot<ТОКЕН>/setWebhook" \
  -d "url=https://bot.example.com/tg" -d "secret_token=$(openssl rand -hex 16)"

Telegram пришлёт его в заголовке X-Telegram-Bot-Api-Secret-Token, aiogram проверит сам. Минус вебхука честный: домен, сертификат с продлением, nginx и ещё один источник отказов.

Лимиты, о которые бьются растущие боты: около 30 сообщений в секунду суммарно, 1 в секунду в один чат, 20 в минуту в одну группу. Превысили — прилетает TelegramRetryAfter с полем retry_after (например, 32 секунды), и на это время бот для вас закрыт. Рассылки поэтому шлют порциями с паузой.

Обновление кода — три команды, простой в секунду:

sudo -u tgbot git -C /opt/tgbot pull
sudo -u tgbot /opt/tgbot/venv/bin/pip install -r /opt/tgbot/requirements.txt
systemctl restart tgbot

Состояние в SQLite копируйте не через cp: sqlite3 /opt/tgbot/data/bot.db ".backup '/opt/tgbot/data/bot-$(date +%F).db'" — копия «на живую» бывает битой; для Postgres берите pg_dump. Cron раз в сутки плюс выгрузка за пределы сервера спасают от потери подписок.

Какой сервер взять в MAATRIX под Telegram-бота

Отталкиваемся от цифры из вывода выше: пустой aiogram-бот занимает около 68 МБ, с парой десятков хендлеров, SQLite и клиентом к внешнему API — 90–150 МБ. Отсюда минимум: 1 vCPU, 1 ГБ RAM, 10–20 ГБ NVMe — хватает боту с тысячами пользователей, он почти всё время ждёт сети, а не считает.

Оговорка: на 1 ГБ обязательно добавьте swap, иначе pip install на тяжёлой зависимости упрётся в OOM-killer и в dmesg появится Out of memory: Killed process. Двух гигабайт достаточно:

fallocate -l 2G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
sysctl -w vm.swappiness=10

Комфортный вариант — 2 vCPU, 4 ГБ RAM, 40–60 ГБ NVMe. Он нужен, когда рядом Postgres или Redis, несколько ботов, очередь задач или обработка фото. Второе ядро принципиально: при одном vCPU долгая синхронная операция в хендлере подвешивает весь event loop, и остальные пользователи ждут. Больше берут под локальную модель, транскрибацию аудио, тяжёлый парсинг.

Локация. Наш дефолт для ботов — UK, Лондон. Первая причина: близость к европейским площадкам Telegram — в наших замерах ping api.telegram.org из Лондона даёт около 10 мс против 40–60 мс из Москвы; на одном сообщении разницы нет, на рассылке в 5000 адресов это минуты. Вторая, важнее: с британского IP работают зарубежные API — платёжные шлюзы, ИИ-провайдеры, почтовые сервисы, не принимающие российские адреса. Если бот завязан на американские ИИ-сервисы, логичнее US (Нью-Йорк) с чистым IP.

Когда UK не подходит, скажем прямо: если бот завязан на российские платёжные и SMS-сервисы, ограничивающие иностранные адреса, и тем более если вы храните персональные данные россиян и обязаны соблюдать 152-ФЗ, база должна жить в RU, там же и минимальный пинг до аудитории. Развести тоже можно: обработчик в UK, база в RU.

Оплата из России — картой российского банка, по СБП, криптовалютой или токеном MAAT; иностранная карта не нужна даже для лондонской площадки. Сервер выдаётся за минуты, дальше полчаса по этой инструкции — и бот живёт сам, переживая перезагрузки и падения. RAM и диск расширяются позже в панели, без переноса бота.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть бот-стек

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Хватит ли 1 ГБ RAM для Telegram-бота на Python?

Да: пустой процесс aiogram занимает около 68 МБ, с базой и хендлерами — 90–150 МБ. Добавьте 2 ГБ swap, иначе установка зависимостей упрётся в OOM-killer.

Почему бот падает с Conflict: terminated by other getUpdates request?

Один токен опрашивают два процесса сразу: обычно копия осталась на локальной машине или продублирован systemd-юнит. Остановите лишний экземпляр — ошибка исчезнет.

Нужен ли домен и SSL для бота?

На long polling нет: входящие соединения не требуются, достаточно открытого 22/tcp. Домен и сертификат нужны только для webhook — его Telegram принимает лишь на портах 443, 80, 88 и 8443.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.