VPSРейтинг
БотыОбновлено: февраль 2026 · 12 мин чтения

Как запустить Telegram-бота на VPS: aiogram 3, systemd, .env

Современный production-деплой: aiogram 3.x вместо устаревшего pyTelegramBotAPI, токен в переменных окружения, автозапуск и graceful shutdown через systemd.

Что понадобится

  • • VPS с Ubuntu 22.04 — минимум 1 vCPU, 512 МБ RAM (этого достаточно)
  • • Root-доступ по SSH
  • • Токен бота от @BotFather в Telegram
  • • Базовые знания Python

Нет VPS? Смотрите VPS для ботов — тариф от 200₽/мес.

1. Polling vs Webhook — что выбрать

Есть два способа получать обновления от Telegram. Выбор влияет на сложность деплоя.

ПараметрPollingWebhook
Как работаетБот спрашивает Telegram: «есть обновления?»Telegram сам шлёт POST на ваш URL
Нужен домен + SSL❌ Нет✅ Обязательно
Задержка доставки~1–3 сек<100 мс
Нагрузка на Telegram APIВыше (постоянные запросы)Минимальная
Сложность настройкиПростаяСредняя (Nginx + SSL)
МасштабируемостьОдин процессНесколько воркеров
Когда использоватьБольшинство ботов>1000 сообщений/день
Вывод: Начните с polling — он проще и достаточен для 99% ботов. Если бот вырастет до 10 000+ пользователей, тогда переходите на webhook. В этом гайде разберём оба способа.

2. Создание бота в BotFather

Если бота ещё нет — создайте его через официального бота Telegram:

1Откройте @BotFather в Telegram
2Отправьте команду /newbot
3Введите имя бота (например: My Awesome Bot)
4Введите username (должен заканчиваться на bot): my_awesome_bot
5BotFather пришлёт токен вида: 5123456789:ABCDefgh... — сохраните его
Токен — это пароль от вашего бота. Никому не передавайте, не публикуйте в коде, не загружайте в GitHub. Если токен утёк — отзовите его командой /revoke у BotFather.

3. Python 3.11 и виртуальное окружение

Никогда не устанавливайте пакеты глобально через pip install без venv. Виртуальное окружение изолирует зависимости бота от системы — разные боты могут использовать разные версии библиотек без конфликтов.

Установка Python 3.11 и создание пользователя
# Обновляем систему
apt update && apt upgrade -y

# Устанавливаем Python 3.11 (в Ubuntu 22.04 есть в базовых репо)
apt install -y python3.11 python3.11-venv python3.11-dev

# Создаём пользователя для бота (не запускаем под root)
useradd -r -m -s /bin/bash botuser
su - botuser
Создаём директорию и виртуальное окружение
mkdir -p ~/mybot
cd ~/mybot

# Создаём venv
python3.11 -m venv venv

# Активируем
source venv/bin/activate

# Теперь pip устанавливает только в venv, не в систему
pip install --upgrade pip
pip install aiogram==3.17.0 python-dotenv
Сохраняем зависимости
pip freeze > requirements.txt
# На другом сервере: pip install -r requirements.txt

4. Структура проекта

Минимальная структура для production-бота. Хендлеры отделены от точки входа — удобно добавлять новые команды не трогая основной файл:

Структура директорий
mybot/
├── venv/                  # виртуальное окружение (не коммитить!)
├── bot.py                 # точка входа
├── config.py              # настройки из .env
├── handlers/
│   ├── __init__.py
│   ├── commands.py        # /start, /help
│   └── messages.py        # ответы на текст
├── .env                   # токен и секреты (не коммитить!)
├── .env.example           # шаблон .env для документации
├── .gitignore
└── requirements.txt
.gitignore — обязательно
.env
.env.local
venv/
__pycache__/
*.pyc
*.pyo
.DS_Store

5. Токен в .env — правильно и безопасно

Единственный правильный способ хранить токен — в переменных окружения. Библиотека python-dotenv загружает их из файла .env:

.env (НИКОГДА не коммитить в git!)
BOT_TOKEN=5123456789:ABCDefghIJKLmnopQRSTuvwxyz12345678
# Дополнительные настройки:
ADMIN_ID=123456789
LOG_LEVEL=INFO
.env.example (коммитить — это шаблон без реальных значений)
BOT_TOKEN=your_bot_token_here
ADMIN_ID=your_telegram_user_id
LOG_LEVEL=INFO
config.py — загрузка настроек
from dotenv import load_dotenv
import os

load_dotenv()

BOT_TOKEN = os.getenv("BOT_TOKEN")
ADMIN_ID = int(os.getenv("ADMIN_ID", "0"))
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")

if not BOT_TOKEN:
    raise ValueError("BOT_TOKEN не задан в .env!")

6. Код бота на aiogram 3.x

Aiogram 3 использует роутеры — каждый хендлер регистрируется на своём роутере, потом роутеры подключаются к диспетчеру. Это удобно для разбивки кода по файлам.

handlers/commands.py
from aiogram import Router
from aiogram.filters import CommandStart, Command
from aiogram.types import Message

router = Router()

@router.message(CommandStart())
async def cmd_start(message: Message):
    await message.answer(
        f"Привет, {message.from_user.full_name}! 👋\n"
        "Я работаю на VPS 24/7.\n\n"
        "Команды:\n"
        "/help — помощь"
    )

@router.message(Command("help"))
async def cmd_help(message: Message):
    await message.answer(
        "Список команд:\n"
        "/start — запустить бота\n"
        "/help — эта справка"
    )
handlers/messages.py
from aiogram import Router, F
from aiogram.types import Message

router = Router()

@router.message(F.text)
async def echo_message(message: Message):
    # Простое эхо — замените на свою логику
    await message.answer(f"Вы написали: {message.text}")
handlers/__init__.py
from .commands import router as commands_router
from .messages import router as messages_router

routers = [commands_router, messages_router]
bot.py — точка входа
import asyncio
import logging
from aiogram import Bot, Dispatcher
from config import BOT_TOKEN, LOG_LEVEL
from handlers import routers

# Настройка логирования
logging.basicConfig(
    level=getattr(logging, LOG_LEVEL),
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger(__name__)


async def main():
    bot = Bot(token=BOT_TOKEN)
    dp = Dispatcher()

    # Подключаем все роутеры
    for router in routers:
        dp.include_router(router)

    logger.info("Бот запускается...")

    # Запускаем polling
    # drop_pending_updates=True — игнорируем накопившиеся сообщения при рестарте
    await dp.start_polling(bot, drop_pending_updates=True)


if __name__ == "__main__":
    asyncio.run(main())
Тестовый запуск (убедиться что работает)
cd ~/mybot
source venv/bin/activate
python bot.py
# INFO: Бот запускается...
# Напишите /start боту в Telegram — должен ответить

Если бот ответил — останавливайте Ctrl+C и переходим к автозапуску.

7. Автозапуск через systemd

Systemd запустит бота при старте сервера и перезапустит если он упадёт.EnvironmentFile загружает .env — токен не нужно прописывать в unit-файле.

Создаём /etc/systemd/system/telegram-bot.service
[Unit]
Description=Telegram Bot (aiogram)
After=network.target

[Service]
Type=simple
User=botuser
WorkingDirectory=/home/botuser/mybot

# Загружаем переменные из .env
EnvironmentFile=/home/botuser/mybot/.env

# Запуск через venv
ExecStart=/home/botuser/mybot/venv/bin/python bot.py

# Перезапуск при любом сбое (кроме Ctrl+C)
Restart=always
RestartSec=5s

# Graceful shutdown: сначала SIGTERM, через 15 сек SIGKILL
KillMode=mixed
TimeoutStopSec=15s

# Логи идут в journald
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
Активируем и запускаем
systemctl daemon-reload
systemctl enable telegram-bot   # автозапуск при загрузке
systemctl start telegram-bot    # запустить сейчас

# Проверяем статус
systemctl status telegram-bot
Полезные команды
# Логи в реальном времени
journalctl -u telegram-bot -f

# Последние 50 строк
journalctl -u telegram-bot -n 50

# Только ошибки
journalctl -u telegram-bot -p err

# Перезапуск после обновления кода
systemctl restart telegram-bot

8. Обновление бота без даунтайма

Рабочий процесс: скопировали новый код → перезапустили сервис. Systemd отправит SIGTERM, aiogram корректно завершит текущие обработчики, потом запустит новую версию. Сообщения пользователей не теряются — Telegram буферизует их на своей стороне.

Вариант А: через rsync с локальной машины

deploy.sh на локальной машине
#!/bin/bash
# Копируем код (исключая venv, .env и кэш)
rsync -avz \
  --exclude='venv/' \
  --exclude='.env' \
  --exclude='__pycache__/' \
  --exclude='*.pyc' \
  ./mybot/ \
  botuser@IP_СЕРВЕРА:/home/botuser/mybot/

# Устанавливаем зависимости и перезапускаем
ssh botuser@IP_СЕРВЕРА "
  cd ~/mybot
  source venv/bin/activate
  pip install -r requirements.txt -q
  sudo systemctl restart telegram-bot
  echo 'Готово!'
"
Делаем скрипт исполняемым и деплоим
chmod +x deploy.sh
./deploy.sh

Вариант Б: через git (если репозиторий настроен)

На сервере — после git pull
cd ~/mybot
git pull origin main
source venv/bin/activate
pip install -r requirements.txt -q
sudo systemctl restart telegram-bot

9. Webhook — для высоконагруженных ботов

Webhook нужен когда бот получает тысячи сообщений в день и важна минимальная задержка. Требует домен + SSL. Используем Nginx как reverse proxy и встроенный сервер aiohttp из aiogram.

Дополнение к .env для webhook

.env (добавить)
WEBHOOK_HOST=https://yourdomain.ru
WEBHOOK_PATH=/webhook
WEBHOOK_SECRET=минимум_32_случайных_символа_здесь
WEBAPP_PORT=8080

bot.py в режиме webhook

bot.py — webhook вариант
import asyncio
import logging
from aiogram import Bot, Dispatcher
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
from aiohttp import web
from config import BOT_TOKEN, LOG_LEVEL
from handlers import routers
import os

load_dotenv()

WEBHOOK_HOST   = os.getenv("WEBHOOK_HOST")
WEBHOOK_PATH   = os.getenv("WEBHOOK_PATH", "/webhook")
WEBHOOK_SECRET = os.getenv("WEBHOOK_SECRET")
WEBAPP_PORT    = int(os.getenv("WEBAPP_PORT", "8080"))
WEBHOOK_URL    = f"{WEBHOOK_HOST}{WEBHOOK_PATH}"

logging.basicConfig(level=getattr(logging, LOG_LEVEL))

async def on_startup(bot: Bot):
    await bot.set_webhook(
        url=WEBHOOK_URL,
        secret_token=WEBHOOK_SECRET,
        drop_pending_updates=True,
    )
    print(f"Webhook установлен: {WEBHOOK_URL}")

async def on_shutdown(bot: Bot):
    await bot.delete_webhook()

def main():
    bot = Bot(token=BOT_TOKEN)
    dp  = Dispatcher()

    for router in routers:
        dp.include_router(router)

    dp.startup.register(on_startup)
    dp.shutdown.register(on_shutdown)

    app = web.Application()
    SimpleRequestHandler(
        dispatcher=dp,
        bot=bot,
        secret_token=WEBHOOK_SECRET,
    ).register(app, path=WEBHOOK_PATH)

    setup_application(app, dp, bot=bot)
    web.run_app(app, host="127.0.0.1", port=WEBAPP_PORT)

if __name__ == "__main__":
    main()

Nginx конфиг для webhook

/etc/nginx/sites-available/yourdomain.ru
server {
    listen 443 ssl;
    server_name yourdomain.ru;

    ssl_certificate     /etc/letsencrypt/live/yourdomain.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.ru/privkey.pem;

    location /webhook {
        proxy_pass         http://127.0.0.1:8080;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto https;
    }
}

server {
    listen 80;
    server_name yourdomain.ru;
    return 301 https://$host$request_uri;
}

Чеклист готовности

  • Токен получен от @BotFather
  • Токен в .env, .env добавлен в .gitignore
  • venv создан, зависимости в requirements.txt
  • python bot.py работает локально — бот отвечает
  • systemd сервис создан и включён (systemctl enable)
  • Бот отвечает после sudo systemctl start telegram-bot
  • Проверен перезапуск после reboot сервера
  • journalctl -u telegram-bot не показывает ошибок

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

Смотрите также