Ви написали Telegram-бота на aiogram, погоняли локально — працює. Тепер йому треба жити там, де не засинають при закритті ноутбука. Невеликий VPS — природний дім, але між python bot.py у SSH-сесії та ботом, який реально тримається через падіння, перезавантаження й обриви зв'язку, є прірва.
Це бойова версія: virtualenv, токен поза кодом, systemd-сервіс, що воскрешає бота сам, і тверезу відповідь на питання, яке рано чи пізно ставить кожен — polling чи webhook?
Чому не лишити на ноутбуці
Боту потрібне стійке вихідне з'єднання з Telegram. Ноутбук засинає, перезавантажується заради оновлень, стрибає між мережами — щоразу бот відпадає, і користувачі впираються в тишу. VPS тримає це з'єднання цілодобово. У цьому весь сенс переїзду з особистої машини — з тієї ж причини і Discord-боту місце на сервері.
Якщо потрібен найшвидший шлях «просто підніми бота» з мінімальним прикладом — це розібрано в гайді з хостингу Telegram-бота. Тут — на рівень глибше: aiogram, безпечний токен і розуміння, коли масштабуватися.
Бот у virtualenv
Заходимо по SSH і тримаємо бота ізольованим у своєму venv — не ставте пакети системно, потім намучаєтесь з оновленнями та чищенням:
sudo apt update && sudo apt install -y python3-venv
mkdir ~/tgbot && cd ~/tgbot
python3 -m venv venv && source venv/bin/activate
pip install -U aiogram
Мінімальний бот на aiogram 3, який читає токен з оточення, а не із зашитого рядка:
# bot.py
import asyncio, logging, os
from aiogram import Bot, Dispatcher
from aiogram.types import Message
from aiogram.filters import CommandStart
logging.basicConfig(level=logging.INFO)
dp = Dispatcher()
@dp.message(CommandStart())
async def start(m: Message):
await m.answer("Живий, працюю на VPS.")
async def main():
bot = Bot(os.environ["BOT_TOKEN"])
await dp.start_polling(bot)
if __name__ == "__main__":
asyncio.run(main())
Токен — поза кодом
Ніколи не вставляйте токен від BotFather прямо в bot.py: один пуш у публічний репозиторій — і він витік. Покладіть його в env-файл, який читає лише root:
sudo tee /etc/tgbot.env >/dev/null <<'EOF'
BOT_TOKEN=123456:ваш-токен-від-botfather
EOF
sudo chmod 600 /etc/tgbot.env
Витеклий токен лагодиться одним /revoke у BotFather — а от витік чого завгодно ще на спільній машині значно гірший. Окремий VPS під бота, чесно кажучи, безпечніший для токена, ніж ваш робочий ноутбук: якщо щось витече, ви міняєте один токен, а не всю свою систему.
Те, що тримає бота живим: systemd
Саме це відділяє бота, який «запускається», від бота, який «тримається». Створюємо сервіс:
# /etc/systemd/system/tgbot.service
[Unit]
Description=Telegram bot (aiogram)
After=network-online.target
Wants=network-online.target
[Service]
User=botuser
WorkingDirectory=/home/botuser/tgbot
EnvironmentFile=/etc/tgbot.env
ExecStart=/home/botuser/tgbot/venv/bin/python bot.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
Запускайте від звичайного користувача (botuser вище), не від root — якщо бота колись скомпрометують, хай він буде замкнений у пісочниці. Далі:
sudo systemctl daemon-reload
sudo systemctl enable --now tgbot
Restart=always з RestartSec=3 означає, що падіння — кривий відповідь Telegram, необроблений виняток, OOM — поверне бота за три секунди, а не лишить лежати, поки ви не помітите. Дивимось логи в реальному часі:
journalctl -u tgbot -f
Ось і все налаштування логування. Жодних файлів логів для ротації, жодних зайвих інструментів — journald уже все пише.
Оновлення без драми з даунтаймом
Коли міняєте код або оновлюєте aiogram:
cd ~/tgbot && source venv/bin/activate
pip install -U aiogram # якщо оновлюєте бібліотеку
sudo systemctl restart tgbot
journalctl -u tgbot -n 30 --no-pager # переконатися, що піднявся чисто
Рестарт мигає ботом на секунду-дві. Для polling-бота це невидимо користувачам — Telegram накопичує апдейти й віддає їх, щойно бот перепідключиться.
Polling проти webhook — чесна версія
Це рішення зазвичай переускладнюють. Ось як є:
Polling (start_polling, як у коді вище) — бот сам питає Telegram «є нове?» довгоживучим з'єднанням. Йому не потрібно нічого, крім вихідного інтернету — ні домену, ні TLS, ні відкритих вхідних портів. Чудово працює за NAT. Для переважної більшості ботів це правильний вибір, і на цьому можна спинитися.
Webhook — Telegram сам шле апдейти вам, а отже треба виставити назовні публічний HTTPS-ендпоінт. Для цього потрібен домен і доступний вхідний порт — тобто або виділений публічний IP, або reverse-proxy попереду. Більше налаштувань, більше того, що може зламатися. Виграш — менша затримка й менше накладних витрат на великому масштабі: тисячі одночасних користувачів, високий потік апдейтів.
Практичне правило: починайте з polling на NAT-тарифі. Переходьте на webhook, лише коли реально переросли polling — і ось тоді виділений IP виправдовує себе, бо вам потрібен цей вхідний HTTPS-ендпоінт.
Скільки це коштує
Polling-бот легкий. Він висить на long-poll Telegram і реагує на повідомлення, тож сервер здебільшого чекає:
- Nano за $3 (1 vCPU / 1 ГБ) — вистачає більшості ботів, навіть доволі балакучим.
- Micro за $5 (2 vCPU / 2 ГБ) — коли бот тримає базу (SQLite/Postgres), працює з медіа або обслуговує багато користувачів.
- Вище — лише якщо бот сам робить реальну роботу: обробка зображень, локальна модель, важка логіка на кожне повідомлення.
Реєстрація за одним email, оплата в USDC або USDT на Base чи Ethereum — без картки, без документів. Картка теж працює, але в онрампу мінімум ~$27, тож для тарифу за $3 зручніше один раз поповнити баланс і списувати продовження з нього. Лише CPU, один дата-центр у Німеччині — для бота неважливо, але варто знати, якщо потрібен був GPU або конкретний регіон.
Чесний підсумок
Telegram-бот — одна з найдешевших речей для самостійного хостингу: сервер за $3, юніт systemd і polling дають бота, який онлайн через падіння й перезавантаження без няньки. За webhook і тариф побільше беріться, лише коли до цього реально примушує масштаб, — а не тому що туторіал сказав, ніби webhook «кращий». Візьміть маленький сервер, тримайте токен в env-файлі, довірте аптайм systemd і — насамперед — проженіть чек-лист безпеки нового VPS, щоб сам сервер був закритий.