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

Переносим Python-бота на сервер правильно: отдельный пользователь, venv, токен вне кода, служба systemd с перезапуском при падении и чтение логов через journalctl.

30 октября 2026 г.·2 мин чтения·telegram, python, systemd

python bot.py в SSH-сессии умирает вместе с сессией. nohup и screen переживают обрыв, но не перезагрузку сервера и не падение бота. Правильный способ — служба systemd: запускается при загрузке, перезапускается при ошибке, пишет логи в журнал. Ниже на примере Python-бота на aiogram; для Node.js отличия в одной строке.

Для экспериментов подойдёт любой VPS с Ubuntu или Debian; на tanaka.host сервер можно взять на день-два, пройти инструкцию и удалить без лишних трат.

Что нужно: VPS с Ubuntu 24.04 или Debian 12 (боту на long polling хватит самого младшего тарифа), токен от @BotFather, SSH. Домен и сертификат для long polling не нужны.

1. Python

apt update && apt upgrade -y
apt install -y python3-venv git

2. Отдельный пользователь

Бот не должен работать под root. Системный пользователь без права входа:

useradd --system --create-home --home-dir /opt/mybot --shell /usr/sbin/nologin mybot

3. Код

Из Git:

sudo -u mybot git clone https://github.com/ВЫ/ВАШ_БОТ.git /opt/mybot/app

Или минимальный бот для проверки:

sudo -u mybot mkdir -p /opt/mybot/app
sudo -u mybot nano /opt/mybot/app/bot.py
import asyncio
import logging
import sys
from os import getenv

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("Привет! Я работаю на VPS.")


@dp.message()
async def echo(message: Message) -> None:
    await message.send_copy(chat_id=message.chat.id)


async def main() -> None:
    bot = Bot(token=getenv("BOT_TOKEN"))
    await dp.start_polling(bot)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, stream=sys.stdout)
    asyncio.run(main())

Токен читается из переменной окружения BOT_TOKEN, в коде его нет — значит, он не утечёт в Git.

4. Виртуальное окружение

sudo -u mybot python3 -m venv /opt/mybot/venv
sudo -u mybot /opt/mybot/venv/bin/pip install aiogram

Для настоящего проекта — pip install -r /opt/mybot/app/requirements.txt.

5. Токен в отдельном файле

nano /etc/mybot.env
BOT_TOKEN=123456789:ТОКЕН_ОТ_BOTFATHER
chmod 600 /etc/mybot.env

Файл читает systemd под root, сам пользователь mybot его не видит.

6. Служба systemd

nano /etc/systemd/system/mybot.service
[Unit]
Description=My Telegram bot
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=0

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

[Install]
WantedBy=multi-user.target

Что здесь важно:

  • User/Group — бот без привилегий;
  • ExecStart вызывает python из venv напрямую, «активировать» окружение не нужно;
  • Restart=always + RestartSec=5 — перезапуск через 5 секунд после любого выхода;
  • StartLimitIntervalSec=0 — systemd не сдастся после серии быстрых падений;
  • PYTHONUNBUFFERED=1 — иначе Python копит вывод в буфере и логи появляются с задержкой;
  • последние три строки — минимальная изоляция от системы.
systemctl daemon-reload
systemctl enable --now mybot
systemctl status mybot

Ожидается active (running). Отправьте боту /start.

7. Логи

journalctl -u mybot -f                    # в реальном времени
journalctl -u mybot -n 100                # последние 100 строк
journalctl -u mybot --since "1 hour ago"
journalctl -u mybot -b                    # с последней загрузки

Обновление

cd /opt/mybot/app
sudo -u mybot git pull
sudo -u mybot /opt/mybot/venv/bin/pip install -r requirements.txt
systemctl restart mybot

Если правили сам .service — перед restart нужен systemctl daemon-reload.

Node.js и Discord-боты

Схема та же: отдельный пользователь, код в /opt/mybot/app, npm ci от имени пользователя, а в службе меняется одна строка:

ExecStart=/usr/bin/node index.js

Несколько ботов — несколько пользователей, env-файлов и служб: shopbot.service, supportbot.service. Они не мешают друг другу и перезапускаются независимо.