# Telegram-бот на VPS: long polling или webhook, что выбрать

> Как запустить Telegram-бота на VPS: сравнение getUpdates и webhook, пример на python-telegram-bot и защита токена. Что делать, если бот не получает сообщения.

- Рубрика: Гайды по играм
- Игра: VPS
- Опубликовано: 2026-11-13T16:00:00+03:00
- Обновлено: 2026-11-13T16:00:00+03:00
- Адрес: https://blog.egnetwork.ru/vps-telegram-bot
- Автор: Команда EGN

Telegram-бот на VPS решает ту же задачу, что и Discord-бот: работает, пока вы спите. Разница в способе получения сообщений. Бот может сам спрашивать Telegram о новых обновлениях или ждать, пока Telegram пришлёт их на ваш адрес. От выбора зависит, нужен ли вам домен, сертификат и открытый порт.

## Коротко

Для большинства ботов выбирайте long polling (`getUpdates`): бот сам запрашивает обновления, домен и сертификат не нужны. Webhook (`setWebhook`) имеет смысл, когда нужна мгновенная доставка и у вас есть HTTPS на порту 443, 80, 88 или 8443. Токен берите у @BotFather, храните в файле окружения и запускайте службой systemd.

## Два способа получать обновления

У бота есть два взаимоисключающих способа получать сообщения. Первый — long polling через метод `getUpdates`: бот держит исходящий запрос к Telegram, и тот отдаёт новые сообщения, как только они появились. Второй — webhook через `setWebhook`: Telegram отправляет каждое обновление HTTPS-запросом POST на адрес вашего сервера.

![Схема: getUpdates — бот запрашивает обновления, setWebhook — Telegram присылает их на ваш сервер](https://blog.egnetwork.ru/media/663)

Официальная документация Bot API прямо указывает: пока webhook установлен, `getUpdates` не работает. Поэтому переключаться между ними нужно аккуратно, сначала отключив старый способ.

Для webhook Telegram требует HTTPS и поддерживает порты 443, 80, 88 и 8443. На VPS это означает домен, сертификат Let's Encrypt и настроенный nginx или другой веб-сервер. Подробно эта схема разобрана в статье [Сайт на VPS с nginx и Let's Encrypt](https://blog.egnetwork.ru/vps-sait-nginx-letsencrypt).

## Что выбрать

Long polling подходит, если бот отвечает на команды и не обрабатывает тысячи сообщений в секунду. Он проще в настройке, работает за NAT и не требует домена. Минус — небольшая задержка и постоянный исходящий запрос, но для обычного бота это незаметно.

Webhook выбирайте, если у вас уже есть HTTPS-сайт, бот принимает много обновлений или вы строите сервис на несколько серверов. В этом случае Telegram не держит запросы к вашей машине, а присылает данные сам.

## Шаги

1. Создайте бота у [@BotFather](https://t.me/BotFather): команда `/newbot`, затем имя и username. BotFather выдаст токен. Сохраните его, но не публикуйте.
2. Подготовьте VPS: Ubuntu 24.04, обновления системы, ключи SSH вместо пароля и пользователь без root для бота.
3. Установите окружение и библиотеку python-telegram-bot версии 21:

```bash
sudo apt update && sudo apt install -y python3-venv
sudo useradd -r -s /usr/sbin/nologin tgbot
sudo mkdir -p /opt/tg-bot && sudo chown tgbot:tgbot /opt/tg-bot
cd /opt/tg-bot
python3 -m venv venv
./venv/bin/pip install "python-telegram-bot>=21,<22"
```

4. Напишите `bot.py`. Токен берётся из окружения:

```python
import os
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Бот работает.")

def main():
    app = Application.builder().token(os.environ["BOT_TOKEN"]).build()
    app.add_handler(CommandHandler("start", start))
    app.run_polling(allowed_updates=Update.ALL_TYPES)

if __name__ == "__main__":
    main()
```

5. Создайте файл `/opt/tg-bot/.env` и положите в него токен (без кавычек):

```properties
BOT_TOKEN=вставьте-токен-из-BotFather
```

```bash
sudo chown tgbot:tgbot /opt/tg-bot/.env
sudo chmod 600 /opt/tg-bot/.env
```

6. Опишите службу `/etc/systemd/system/tg-bot.service`:

```ini
[Unit]
Description=Telegram bot
After=network-online.target
Wants=network-online.target

[Service]
User=tgbot
WorkingDirectory=/opt/tg-bot
EnvironmentFile=/opt/tg-bot/.env
ExecStart=/opt/tg-bot/venv/bin/python bot.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
```

7. Запустите и проверьте журнал:

```bash
sudo systemctl daemon-reload
sudo systemctl enable --now tg-bot
sudo journalctl -u tg-bot -f
```

8. Отправьте боту команду `/start` и проверьте ответ.

![Верх карточки VPS: кнопка «Консоль» открывает терминал, где можно выполнять команды настройки](https://blog.egnetwork.ru/media/612)

## Переход с webhook на polling

Если бот раньше работал через webhook, а теперь вы переходите на long polling, сначала отключите webhook. Метод `deleteWebhook` делает это одним запросом к Bot API. Иначе бот будет получать ошибку при каждом `getUpdates`, а сообщения будут уходить на старый адрес.

Проверить состояние можно методом `getWebhookInfo`: если в ответе есть непустой `url`, webhook ещё активен.

## Примеры

**Команда «/start» в группе.** В группе при включённом privacy mode (так по умолчанию) бот видит только команды, ответы на свои сообщения и упоминания. Чтобы он читал все сообщения группы, отключите privacy mode в BotFather командой `/setprivacy` или сделайте бота администратором группы.

**Перезапуск после ошибки.** Если процесс упал из-за необработанного исключения, `Restart=always` поднимает его через пять секунд. Кратковременные сетевые сбои при long polling библиотека в основном переживает сама, но не рассчитывайте на это и смотрите журнал.

**Утёкший токен.** Команда `/revoke` в BotFather выпускает новый токен. Старый перестаёт работать, а бот нужно обновить в `.env` и перезапустить службу.

## Симптом → причина → решение

| Симптом | Частая причина | Что сделать |
|---|---|---|
| Бот не отвечает на `/start` | Служба не запущена или упала | `sudo systemctl status tg-bot`, затем журнал |
| `Conflict: terminated by other getUpdates request` | Запущены два экземпляра с одним токеном | Остановите лишний процесс: `ps aux \| grep bot.py` |
| `InvalidToken` или `401 Unauthorized` в журнале | Токен неверный или отозван | Получите новый токен у BotFather (`/token` или `/revoke`) и обновите `.env` |
| Сообщения не приходят после webhook | Webhook всё ещё активен | Вызовите `deleteWebhook`, затем перезапустите бота |
| Бот не получает обновления, в журнале таймауты | С VPS нет доступа к `api.telegram.org` | Проверьте `curl -sS -m 10 https://api.telegram.org`, при необходимости смените локацию или сеть |
| Ответы приходят с задержкой | Тяжёлый код в обработчике | Вынесите долгие операции в фоновую задачу |

## Пример: проверка webhook одной командой

Если вы не уверены, работает ли webhook, проверьте его через `getWebhookInfo`. Токен подставляется из файла окружения, поэтому в историю команд попадает только имя переменной. Выполняйте команду от root или от пользователя, который может читать `.env`.

```bash
source /opt/tg-bot/.env
curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getWebhookInfo"
```

В ответе поле `url` должно быть пустым при long polling. Не публикуйте вывод целиком: он безопасен, но привычка не светить URL с токеном (`.../bot<токен>/...`) в чатах и тикетах спасает от утечек.

## Пример: ответ с клавиатурой

Команда `/menu` показывает кнопки, а нажатие обрабатывается отдельным обработчиком. Так бот выглядит живее, чем с набором текстовых команд:

```python
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CallbackQueryHandler

async def menu(update, context):
    keyboard = [[InlineKeyboardButton("Статус", callback_data="status")]]
    await update.message.reply_text("Выберите действие:", reply_markup=InlineKeyboardMarkup(keyboard))

async def on_button(update, context):
    query = update.callback_query
    await query.answer()
    await query.edit_message_text("Бот работает.")

# в main():
# app.add_handler(CommandHandler("menu", menu))
# app.add_handler(CallbackQueryHandler(on_button))
```

Кнопки с `callback_data` обрабатываются через `CallbackQueryHandler`. Не забывайте вызывать `query.answer()`, иначе у пользователя будет крутиться индикатор ожидания.

## Чек-лист перед запуском

Перед тем как пустить бота к пользователям, проверьте:

1. Токен хранится в `.env` с правами 600, а в коде вызывается через `os.environ`.
2. Служба стартует после `reboot` и перезапускается после падения.
3. Бот отвечает на `/start` и на одну команду с ошибкой, например на неверный ввод. Ошибка не должна ронять процесс.
4. В журнале нет исключений при обычной работе: `journalctl -u tg-bot --since "1 hour ago"`.
5. Команда `/revoke` в BotFather известна тем, кто за ботом следит: она нужна при утечке.
6. Если используется webhook, сертификат действителен, а `getWebhookInfo` не содержит ошибок в поле `last_error_message`.

## Что делать, если бот начал падать

Сначала посмотрите журнал за последний час. Если ошибка сетевая, например таймаут до Telegram, это временная проблема, и служба сама повторит запрос. Если исключение в вашем коде, поправьте обработчик и перезапустите службу. Если бот падает раз в несколько минут без причины, проверьте память командой `free -h`: процесс мог не хватать памяти и его остановило ядро.

## Частые ошибки

- **Бот молчит, хотя служба работает.** Смотрите журнал: `Conflict: terminated by other getUpdates request` означает, что запущены два экземпляра бота с одним токеном.
- **Webhook включён, а polling не работает.** Вызовите `deleteWebhook`, затем перезапустите бота.
- **Токен в коде или в репозитории.** Такой токен надо отзывать через `/revoke`. Держите его в `.env` с правами 600.
- **Нет доступа к api.telegram.org с VPS.** Бот сам обращается к Telegram, поэтому доступность API проверяйте именно с сервера: `curl -sS -m 10 https://api.telegram.org`. Если запрос не проходит, смотрите сеть, DNS и локацию сервера. Доступность самого мессенджера для ваших пользователей от этого не зависит.
- **Webhook без HTTPS.** Telegram принимает webhook только по HTTPS на портах 443, 80, 88 или 8443. Для теста используйте long polling.
- **Служба падает при старте.** Проверьте, что путь в `ExecStart` указывает на Python внутри venv, а `.env` читается пользователем tgbot.

## Итог

Telegram-бот на VPS проще всего запустить через long polling: отдельный пользователь, venv, файл `.env` и служба systemd с `Restart=always`. Webhook добавляет домен и HTTPS, зато даёт мгновенную доставку. Выбирайте по задаче, а не по моде, и не забывайте про `deleteWebhook`, если меняете способ получения обновлений.

## Вопросы и ответы

### Можно ли запускать Telegram-бота на домашнем компьютере?
Можно, но он будет работать только при включённой машине и стабильном интернете. VPS решает это.

### Long polling нагружает сервер?
Нет, для обычного бота нагрузка минимальна. Long polling держит один исходящий запрос.

### Нужен ли домен для long polling?
Нет. Домен и сертификат нужны только для webhook.

### Что такое privacy mode?
Это настройка, из-за которой бот в группе видит только команды и упоминания. Меняется в BotFather командой `/setprivacy`.

Сервер под игры за пару минут — [egnetwork.ru](https://egnetwork.ru/)

## Официальные источники

- [@BotFather](https://t.me/BotFather)
- [api.telegram.org`](https://api.telegram.org`/)

---

Материал EGN (blog.egnetwork.ru), обновлено 13 ноября 2026; при цитировании указывайте ссылку.
