Как поднять Telegram-бота на Python на VPS
Бот, запущенный с ноутбука, живёт ровно до закрытия крышки: пропал Wi-Fi — пропал сервис. Telegram-бот на VPS снимает это за вечер: чистая Ubuntu, venv, aiogram 3 и служба systemd, которая поднимает процесс после падения и перезагрузки. Ниже — весь путь с командами, конфигами и текстами ошибок, на которых застревают.
Содержание
- Что подготовить до подключения к серверу
- Базовая подготовка Ubuntu: пользователь, SSH, ufw
- Python, venv и грабля externally-managed-environment
- Минимальный бот на aiogram 3 и первый запуск руками
- systemd-служба: автозапуск, перезапуск и ограничение прав
- Webhook, лимиты Telegram и обновление без простоя
- Какой сервер взять в MAATRIX под Telegram-бота
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество 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 — десятки моделей в одном окне. Оплата картой РФ и по СБП.