Чтобы Telegram-бот отвечал круглосуточно, его нужно запускать на сервере, который не выключается вместе с вашим компьютером. VPS для этого подходит лучше всего: у вас есть свой Linux, доступ по SSH и полный контроль над процессом. В этой статье бот на Python и aiogram 3 работает в виртуальном окружении, токен лежит в файле .env, а systemd перезапускает процесс после сбоя и поднимает его после перезагрузки сервера. Если вы еще не подключались к серверу, начните с инструкции как подключиться к VPS по SSH.
Что понадобится
- VPS на Ubuntu 24.04 или Debian 12 с доступом по SSH.
- Токен бота, который выдает @BotFather при команде /newbot.
- Python 3.11 или новее. В Ubuntu 24.04 и Debian 12 он уже установлен в системе.
- Базовое знакомство с командной строкой Linux.
Шаг 1. Подготовка сервера
Обновите систему и установите пакеты для виртуального окружения Python. Пакет python3-venv в Ubuntu и Debian вынесен отдельно, без него команда venv не работает.
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-venv python3-pip gitСоздайте отдельного системного пользователя без права входа в оболочку. Бот не должен работать от root: если токен утечет, злоумышленник не получит доступ ко всему серверу, а только к процессу бота.
sudo useradd --system --create-home --home-dir /opt/bot --shell /usr/sbin/nologin botШаг 2. Код бота на aiogram 3
Создайте виртуальное окружение в каталоге /opt/bot и установите библиотеки. Виртуальное окружение изолирует зависимости бота от системного Python, поэтому обновление системы не сломает бота.
sudo python3 -m venv /opt/bot/venv
sudo /opt/bot/venv/bin/pip install --upgrade pip aiogram python-dotenv
sudo chown -R bot:bot /opt/botСоздайте файл /opt/bot/bot.py. Это минимальный бот: он отвечает на команду /start и повторяет текст сообщения. Логика вашего бота будет добавляться в обработчики.
import asyncio
import logging
import os
from aiogram import Bot, Dispatcher, F
from aiogram.filters import CommandStart
from aiogram.types import Message
from dotenv import load_dotenv
load_dotenv('/opt/bot/.env')
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
BOT_TOKEN = os.environ['BOT_TOKEN']
dp = Dispatcher()
@dp.message(CommandStart())
async def cmd_start(message: Message) -> None:
await message.answer('Бот запущен на VPS. Напишите любое сообщение.')
@dp.message(F.text)
async def echo(message: Message) -> None:
await message.answer(message.text)
async def main() -> None:
bot = Bot(token=BOT_TOKEN)
await dp.start_polling(bot)
if __name__ == '__main__':
asyncio.run(main())Этот код использует синтаксис aiogram 3: фильтры передаются в декоратор dp.message, а сам бот создается с токеном при запуске. В старых примерах из aiogram 2 встречаются другие имена, их лучше не копировать.
Когда бот вырастет, обработчики удобнее разнести по модулям. В aiogram 3 для этого используется класс Router: каждый модуль создает свой router, а главный Dispatcher подключает их через include_router. Так код остается читаемым, и новые команды не требуют правок в одном большом файле.
Шаг 3. Токен в файле .env
Токен нельзя писать прямо в код и тем более коммитить в Git. Создайте файл .env и вставьте в него токен, который вы получили у @BotFather. Значение ниже является заглушкой.
BOT_TOKEN=000000000:REPLACE_WITH_TOKEN_FROM_BOTFATHERsudo nano /opt/bot/.env
sudo chown bot:bot /opt/bot/.env
sudo chmod 600 /opt/bot/.envПрава 600 означают, что читать файл может только его владелец, то есть пользователь bot. Если токен попал в чат, в публичный репозиторий или на скриншот, откройте @BotFather, отправьте команду /revoke, выберите бота и обновите значение в .env.
Шаг 4. Автозапуск через systemd
Запуск python bot.py в обычной SSH-сессии быстро работает, но процесс остановится, когда вы закроете соединение или сервер перезагрузится. Systemd решает обе задачи. Создайте unit-файл /etc/systemd/system/bot.service.
[Unit]
Description=Telegram bot (aiogram)
After=network-online.target
Wants=network-online.target
[Service]
User=bot
Group=bot
WorkingDirectory=/opt/bot
ExecStart=/opt/bot/venv/bin/python /opt/bot/bot.py
Restart=always
RestartSec=5
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetПараметр Restart=always перезапускает процесс при любом завершении: из-за ошибки, исключения или сигнала. RestartSec=5 делает паузу пять секунд, чтобы сбойный бот не уходил в быстрый бесконечный цикл перезапусков. Затем перечитайте конфигурацию systemd и включите сервис.
sudo systemctl daemon-reload
sudo systemctl enable --now bot
sudo systemctl status botКоманда enable добавляет автозапуск после перезагрузки сервера, а --now сразу запускает сервис. Проверьте, что в строке Active стоит active (running).
Логи и диагностика
Systemd собирает вывод процесса в журнал. Все сообщения из logging и ошибки Python попадают туда автоматически, отдельные файлы логов создавать не нужно.
journalctl -u bot -f # логи в реальном времени
journalctl -u bot --since "1 hour ago" # за последний час
journalctl -u bot -n 100 --no-pager # последние 100 строк
journalctl -u bot -p err # только ошибкиЕсли сервис постоянно перезапускается, Restart=always может скрыть проблему: бот будто работает, но не отвечает. Смотрите systemctl status bot и журнал. Чаще всего причина в неверном токене, в отсутствующей библиотеке или в ошибке в коде.
В коде пишите сообщения через logging и не выводите токен или персональные данные пользователей. Для ошибок внутри обработчиков используйте logging.exception, потому что он добавляет в журнал полный traceback. Уровень INFO подходит для обычных событий, а WARNING для неожиданных ситуаций, которые бот смог обработать.
Проверить состояние без чтения журнала можно командой systemctl is-active bot: она выводит active или failed. Эту проверку удобно добавить в простой скрипт по cron, который пришлет уведомление, если бот остановился.
Обновление бота
Обновление занимает несколько команд: загрузите новый код, обновите зависимости, если они менялись, и перезапустите сервис. Файл requirements.txt создайте один раз командой pip freeze, чтобы версии библиотек были одинаковыми на всех серверах.
sudo /opt/bot/venv/bin/pip freeze > /opt/bot/requirements.txt
sudo -u bot git -C /opt/bot pull
sudo /opt/bot/venv/bin/pip install -r /opt/bot/requirements.txt
sudo systemctl restart bot
journalctl -u bot -n 50 --no-pagerЕсли код не хранится в Git, загрузите новый bot.py через SFTP или scp в /opt/bot и выполните последние две команды. Перед обновлением на рабочем боте имеет смысл сохранить копию текущего каталога.
Webhook или polling
Бот может получать обновления двумя способами. Polling, как в примере выше, означает, что бот сам регулярно запрашивает новые сообщения у Telegram. Это проще: не нужен домен, HTTPS-сертификат и открытые входящие порты. Для большинства ботов с умеренной нагрузкой этого достаточно.
Webhook означает, что Telegram сам отправляет обновления на ваш HTTPS-адрес. Такой режим экономнее при большом потоке сообщений, но требует домена с действующим сертификатом и открытого порта из списка, который поддерживает Telegram: 443, 80, 88 или 8443. Настройку сайта и SSL разберите в статье как разместить сайт на VPS с Nginx и SSL. Для первого бота выбирайте polling.
Частые ошибки
- Токен записан в коде или попал в Git. Отзовите его через /revoke и выпустите новый.
- Бот запущен от root. Используйте отдельного пользователя bot, как в этой инструкции.
- Сервис не стартует после перезагрузки. Проверьте командой systemctl is-enabled bot, что автозапуск включен.
- Библиотеки установлены не в виртуальное окружение. Для запуска используется путь /opt/bot/venv/bin/python.
Итоги
Бот на VPS состоит из четырех частей: виртуальное окружение с aiogram 3, файл .env с токеном, unit-файл systemd с Restart=always и журнал journalctl для диагностики. Такой набор переживает падения процесса и перезагрузки сервера, а обновление сводится к загрузке кода и перезапуску. Для одного бота Docker не обязателен, но если вы захотите упаковать несколько сервисов, посмотрите статью Docker на VPS. Выбрать подходящий тариф поможет раздел тарифы VPS.