Telegram-бот на Python на VPS круглосуточно: venv, systemd, логи, автоперезапуск
Как перенести бота с ноутбука на арендованный сервер, чтобы он работал без вашего участия: отдельный пользователь, виртуальное окружение и aiogram, токен в файле окружения, служба systemd с автозапуском и перезапуском после сбоя, логи в journalctl и их предел на диске, обновление кода и разбор типичных ошибок. Ubuntu 24.04, сентябрь 2026.
Что получится и что понадобится
Бот будет запускаться вместе с сервером, подниматься сам после падения и писать логи в системный журнал. Для этого нужны четыре вещи: свежий сервер с Ubuntu 24.04, на котором сделаны первые шаги из инструкции про настройку VPS (пользователь с sudo, ключи, файрвол), токен бота от @BotFather, сам код бота и полчаса времени. Бот в примере написан на aiogram 3, но служба systemd одинаково запускает python-telegram-bot, Telethon и всё остальное, что стартует одной командой.
Команды сверены с документацией Python (модуль venv), systemd (systemd.service, systemd.exec, journalctl) и aiogram 3 для Ubuntu 24.04, где Python 3.12 стоит из коробки. На Ubuntu 26.04 команды те же, но версия Python другая, проверьте на своей системе. Адрес сервера и токен в примерах условные.
Шаг 1. Отдельный пользователь и папка
Бот не должен работать от root: ошибка в коде или в чужой библиотеке тогда останется в пределах одной папки. Создайте системного пользователя без пароля и входа по SSH, его домашним каталогом будет папка бота.
sudo apt update && sudo apt install -y python3-venv git
sudo adduser --system --group --home /opt/tgbot tgbotВсё, что лежит в /opt/tgbot, должно принадлежать этому пользователю. Если вы копируете код по scp или клонируете репозиторий от своего имени, после этого поправьте владельца: sudo chown -R tgbot:tgbot /opt/tgbot.
Шаг 2. Python, виртуальное окружение и aiogram
Системный Python в Ubuntu трогать нельзя: пакет python3 нужен самой системе, а pip откажется ставить что-либо мимо виртуального окружения. Поэтому окружение создаётся внутри папки бота, и все зависимости живут в нём.
sudo -u tgbot python3 -m venv /opt/tgbot/venv
sudo -u tgbot /opt/tgbot/venv/bin/pip install --upgrade pip aiogramЕсли у бота есть requirements.txt, ставьте из него: sudo -u tgbot /opt/tgbot/venv/bin/pip install -r /opt/tgbot/requirements.txt. Активировать окружение командой source на сервере не нужно: достаточно вызывать интерпретатор по полному пути /opt/tgbot/venv/bin/python, так же его будет вызывать и systemd.
Для проверки хватит бота из десяти строк. Сохраните его в /opt/tgbot/bot.py. Токен он берёт из переменной окружения, а не из кода, это важно для следующего шага.
import asyncio
import logging
import os
from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.types import Message
dp = Dispatcher()
@dp.message(CommandStart())
async def start(message: Message) -> None:
await message.answer("Бот работает на сервере")
async def main() -> None:
logging.basicConfig(level=logging.INFO)
bot = Bot(token=os.environ["BOT_TOKEN"])
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())Бот работает через long polling: сам опрашивает серверы Telegram и не требует ни домена, ни сертификата, ни открытых портов. Webhook быстрее отдаёт обновления и экономит трафик, но ему нужен HTTPS, это отдельная настройка.
Шаг 3. Токен в файле окружения, не в коде
Токен в bot.py рано или поздно уезжает в git или в скриншот. Правильное место для него — файл, который читает только пользователь бота. Формат простой, переменная и значение через знак равенства, без пробелов и кавычек; вторая строка заставляет Python писать в лог сразу, а не копить строки в буфере, иначе в journalctl сообщения появятся с задержкой в минуты.
sudo -u tgbot tee /opt/tgbot/.env > /dev/null <<'EOF'
BOT_TOKEN=123456789:AAExampleTokenFromBotFather
PYTHONUNBUFFERED=1
EOF
sudo chmod 600 /opt/tgbot/.envПрежде чем заводить службу, запустите бота руками с этим файлом и отправьте ему /start. Так вы отделите ошибки кода от ошибок systemd. Проверочный запуск останавливается по Ctrl+C.
sudo -u tgbot bash -c 'set -a; source /opt/tgbot/.env; set +a; /opt/tgbot/venv/bin/python /opt/tgbot/bot.py'Если бот молчит, проверьте с сервера сам токен и доступность API Telegram одной командой. В ответ на верный токен придёт JSON с именем бота, на неверный — ошибка 401 Unauthorized. Ошибка соединения или долгое ожидание означают, что с этого сервера до api.telegram.org не добраться, и никакая настройка службы это не исправит: проверьте файрвол хостера, а если сеть в порядке, задачу решает сервер на другой площадке.
curl -sS https://api.telegram.org/bot123456789:AAExampleTokenFromBotFather/getMeШаг 4. Служба systemd: автозапуск и перезапуск
Служба описывается одним файлом. Раздел [Unit] говорит, когда стартовать (после появления сети), [Service] задаёт, кем и как запускать, [Install] отвечает за автозагрузку. Restart=always перезапускает бота после любого завершения, в том числе после выхода без ошибки, потому что для бота любое завершение — авария. RestartSec даёт паузу, чтобы при мёртвом токене процесс не дёргался каждую секунду. Две последние строки раздела [Service] запрещают процессу повышать права и дают ему отдельный /tmp; на работу бота они не влияют.
sudo tee /etc/systemd/system/tgbot.service > /dev/null <<'EOF'
[Unit]
Description=Telegram bot (aiogram)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=tgbot
Group=tgbot
WorkingDirectory=/opt/tgbot
EnvironmentFile=/opt/tgbot/.env
ExecStart=/opt/tgbot/venv/bin/python /opt/tgbot/bot.py
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOFПосле создания или правки файла systemd нужно перечитать конфигурацию, затем включить службу в автозагрузку и сразу запустить.
sudo systemctl daemon-reload
sudo systemctl enable --now tgbot
systemctl status tgbotВ выводе status должно быть active (running) и строка Main PID с номером процесса. Убедиться, что автоперезапуск работает, можно грубо: убейте процесс командой sudo kill -9 <PID> и через пять секунд снова посмотрите status — PID будет новый. Перезагрузка сервера командой sudo reboot проверит автозапуск: после входа бот уже должен отвечать.
Шаг 5. Логи: смотреть, фильтровать, не забивать диск
Всё, что бот пишет в stdout и stderr, попадает в журнал systemd. Отдельные файлы логов и их ротация не нужны. Три команды закрывают почти все случаи: смотреть журнал вживую, показать последние двести строк, показать только ошибки за сегодня.
journalctl -u tgbot -f
journalctl -u tgbot -n 200 --no-pager
journalctl -u tgbot -p err --since todayМесто под записи ограничено само по себе: journald по умолчанию не занимает больше десятой части раздела и столько же оставляет свободным. На сервере с диском 10 ГБ это до гигабайта логов. Если бот многословен, потолок стоит снизить явно: в файле /etc/systemd/journald.conf раскомментируйте строку SystemMaxUse и поставьте, например, 200M, затем перезапустите journald командой sudo systemctl restart systemd-journald. Сколько занимает журнал сейчас, показывает journalctl --disk-usage.
Уровень подробности задаётся в коде: logging.INFO из примера пишет каждое обновление, для рабочего бота обычно хватает WARNING, а INFO включают на время разборов.
Обновление кода и типичные ошибки
Новую версию бота кладут в ту же папку и перезапускают службу; если менялись зависимости, перед перезапуском ставят их в то же окружение. Правка самого файла службы требует daemon-reload, после правки кода и .env достаточно перезапуска.
sudo -u tgbot git -C /opt/tgbot pull
sudo -u tgbot /opt/tgbot/venv/bin/pip install -r /opt/tgbot/requirements.txt
sudo systemctl restart tgbotUnauthorized в логе. Токен не тот или отозван в @BotFather. Проверьте его командой getMe из шага 3, после правки .env перезапустите службу.
TelegramConflictError, terminated by other getUpdates request. Бота с этим токеном опрашивают два процесса: скорее всего, старый экземпляр ещё крутится на ноутбуке или запущен второй раз руками. Telegram отдаёт обновления только одному, остановите лишний.
ModuleNotFoundError: No module named 'aiogram'. Служба запускает системный python вместо интерпретатора из venv. Проверьте путь в ExecStart: он должен начинаться с /opt/tgbot/venv/bin/.
Permission denied на файле или папке. Код или .env принадлежат root, а служба работает от tgbot. Лечится sudo chown -R tgbot:tgbot /opt/tgbot.
status показывает activating (auto-restart) по кругу. Процесс падает сразу после старта. Причина всегда в первых строках journalctl -u tgbot -n 50: обычно это одна из ошибок выше или синтаксическая ошибка в коде.
Бот работает, но в логах пусто. Без PYTHONUNBUFFERED=1 в .env Python копит вывод в буфере и сбрасывает его редко. Добавьте строку и перезапустите службу.
Какой сервер под это нужен
Бот на aiogram, который отвечает на команды и ходит в пару внешних API, занимает десятки мегабайт памяти и почти не грузит процессор. Ему хватает самого маленького тарифа, лишь бы там был Linux и постоянный IP-адрес. В нашем каталоге на 14 сентября 2026 в России продаётся 190 помесячных Linux-тарифов без видеокарты у 11 провайдеров, из них 8 стоят до 300 ₽ в месяц. Самый дешёвый с 1 ГБ памяти — VDSina «Standard 1/1/10» за 150 ₽ (1 vCPU, 1 ГБ, 10 ГБ NVMe); с 512 МБ есть RuVDS «Старт» за 149 ₽, но на полугигабайте стоит сразу включить swap по инструкции про первую настройку. Если с площадки в России до API Telegram не добраться, подойдут те же тарифы у площадок в Европе. Подборка под эту задачу — пресет «VPS-сервер для Telegram-бота» в каталоге: от 512 МБ памяти, Linux, Россия и Европа.