To host an aiogram bot 24/7, run it on a machine that stays on instead of your PC: a Linux VPS with a systemd service that starts it again after a crash or a reboot, or a PlayNHost bot plan (from US$1.99 a month, see Telegram bot hosting), where you upload your code, paste the token and press Start. In our test an aiogram 3.31.0 bot used about 173 MB of memory at rest; our smallest bot plan, STARTER, has 0.5 GB. Both routes use long polling: dp.start_polling(bot) asks Telegram for new messages, so the bot needs no public address. The PlayNHost steps:
- In Telegram, send
/newbotto @BotFather and copy the token it gives you. - On Telegram bot hosting, click Create Server on a plan. In the order window, answer What is your bot for? with Telegram and choose Python.
- Open your server in the panel. The Set up your bot card on Overview opens at Connect to Telegram; its first step, What runs, should say This bot runs Python Bot.
- At Connect to Telegram, paste the token and click Connect. Check the bot name shown and click Yes, continue.
- At Your code, click Upload files to open Files and upload
bot.pyandrequirements.txt, or click Connect Git. - On the Bot page, pane Code, check that Entry file is the file that runs
asyncio.run(main())(bot.pyby default). - Click Start bot, open Console and wait for aiogram's line
Run polling for bot @your_bot. The chip on Overview turns to Online in Telegram. Send your bot/start.
The first start takes longer while pip installs aiogram and its dependencies. After that it installs again only when your requirements file, the extra packages or the Python version change, or once a day. Step by step with screenshots: How to host a Telegram bot.
What we measured
We ran the three Python Telegram libraries on one of our own servers, in throwaway containers set up the way the panel runs a Python bot: Python 3.12 (the Runtime marked recommended), packages installed with the same pip command, a 1 GB memory limit and one CPU core.
| Library | Version | Memory at rest | Start to Telegram's answer | Installed size, with dependencies |
|---|---|---|---|---|
| aiogram | 3.31.0 | 173 MB | 4.3 s | 33.9 MB |
| python-telegram-bot | 22.8 | 44 MB | 0.9 s | 12.3 MB |
| pyTelegramBotAPI | 4.37.1 | 40 MB | 0.7 s | 21.9 MB |
| Python 3.12 alone | 9 MB |
Each bot loaded the library and asked Telegram who it is with a fake token (aiogram: bot.get_me()). Memory is what the bot's processes held 15 seconds after Telegram refused the token; start time runs from launching Python to the library reporting that answer, so it includes one round trip to Telegram. Both are the median of three runs after a warm-up run.
These figures are the library's floor: no chats, no handlers at work, nothing cached. Your own code, the data it keeps in memory (for example conversation states in aiogram's memory storage) and the number of chats it serves come on top, so size the plan by what your bot does. Our older page python-telegram-bot vs aiogram on PlayNHost measured a different setup (Python 3.13, a bot built and ready to start), so its numbers are not comparable with these.
A minimal aiogram bot
It answers /start, reads the token the panel gives it and logs a ready line. Put the library in requirements.txt:
aiogram>=3,<4
And the code in bot.py:
import asyncio
import logging
import os
import sys
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("Hello! I am running on PlayNHost.")
async def main() -> None:
bot = Bot(os.environ["BOT_TOKEN"])
async with bot.context():
await bot.delete_webhook()
await dp.start_polling(bot)
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
asyncio.run(main())
It reads BOT_TOKEN, the name aiogram's own examples use; the panel gives your code the token under that name as well as TELEGRAM_BOT_TOKEN, TOKEN and TELEGRAM_TOKEN. Logging at INFO makes aiogram print a line that starts INFO:aiogram.dispatcher:Run polling for bot @your_bot once Telegram has accepted the token, and the panel reads that line to show Online in Telegram, so you do not need a print of your own. delete_webhook() removes a webhook left from another host before polling starts. async with bot.context() closes the bot's connection to Telegram when main() ends, also when it ends with an error.
If the token is wrong
In our test, with a token Telegram refuses, our test bot printed:
TelegramUnauthorizedError: Telegram server says - Unauthorized
In a full traceback the class reads aiogram.exceptions.TelegramUnauthorizedError. The first call to Telegram fails (in the bot above, delete_webhook(); without it, the getMe that start_polling makes), so the bot stops at once with exit code 1. On PlayNHost the panel shows Telegram did not accept your bot token above the console, with Replace the token.
To fix it, copy the token again: in @BotFather send /mybots, choose your bot, then API Token. Paste it on the Bot page, pane Telegram, under Token, and start the bot again.
Two other cases look different. A token with a space in it, or without the bot's number and a colon in front of the secret, fails before any request: Bot() checks the shape and raises TokenValidationError with a message that starts Token is invalid!. A token revoked while the bot is already polling does not stop it: aiogram logs Failed to fetch updates - TelegramUnauthorizedError and tries again every few seconds, so the process looks running while nothing answers. The panel banner is how you notice.
What a host should know about aiogram
- Package and import names: both are
aiogram. It installs aiohttp, pydantic, magic-filter, aiofiles and certifi with it. - Python version: aiogram 3.31 needs Python 3.10 to 3.14. The Runtime on the Code pane offers exactly 3.10 to 3.14, with 3.12 marked recommended.
- aiogram 2 code does not run on aiogram 3: the
Executorwas removed, soexecutor.start_polling(dp)becomesawait dp.start_polling(bot). A tutorial whoserequirements.txtpins a 2.x version, such asaiogram==2.25.2, is version 2 code; see the question below. - A webhook left from another host:
start_pollingdoes not remove it, and Telegram refuses polling while one is set, so aiogram logsTelegramConflictErrorand retries. Keepawait bot.delete_webhook()as above, or click Remove webhook on the Bot page, pane Telegram. - Logging: without
logging.basicConfigaiogram'sINFOlines, including the ready line, are not shown. AtINFOit also logs one line per update (Update id=... is handled); on a busy bot addlogging.getLogger("aiogram.event").setLevel(logging.WARNING), which keeps the ready line. - Which updates arrive: unless you pass
allowed_updates,start_pollingasks Telegram only for the update types you have handlers for. - A second copy: when another copy polls with the same token, Telegram answers
409 Conflictand aiogram retries for as long as it runs. The panel shows Your bot is running somewhere else too. See Fix Telegram bot 409 Conflict.
Host it yourself on a Linux VPS
On Debian or Ubuntu, install Python, create a user for the bot and install aiogram in a virtual environment. Check that python3 --version says 3.10 to 3.14.
sudo apt update
sudo apt install -y python3 python3-venv
sudo useradd --system --create-home --home-dir /opt/tgbot --shell /usr/sbin/nologin tgbot
sudo cp bot.py requirements.txt /opt/tgbot/
sudo -u tgbot python3 -m venv /opt/tgbot/venv
sudo -u tgbot /opt/tgbot/venv/bin/pip install -r /opt/tgbot/requirements.txt
Put the token in a file only root can read. Open it with sudo nano /etc/tgbot.env, write one line, save, then run sudo chmod 600 /etc/tgbot.env:
BOT_TOKEN=123456789:your-token-from-botfather
Create /etc/systemd/system/tgbot.service:
[Unit]
Description=Telegram bot (aiogram)
Wants=network-online.target
After=network-online.target
[Service]
User=tgbot
WorkingDirectory=/opt/tgbot
EnvironmentFile=/etc/tgbot.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/tgbot/venv/bin/python /opt/tgbot/bot.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Start it now and at every boot, then follow its output:
sudo systemctl daemon-reload
sudo systemctl enable --now tgbot
sudo journalctl -u tgbot -f
The logging lines reach the journal as they happen; PYTHONUNBUFFERED=1 does the same for any print() you add, which Python otherwise holds in a buffer when it does not write to a terminal. systemctl stop tgbot sends SIGTERM, which start_polling handles by stopping polling and closing the bot's session. After you change the code, run sudo systemctl restart tgbot. Stop any copy on your PC first, or Telegram answers 409 Conflict.
Self-hosting or a bot plan
- On a VPS you manage: system updates, the Python version, the virtual environment, reinstalling packages, the unit file, reading the journal and noticing when the bot stops answering. In return you have root, and you can run aiogram's webhook mode if you add a domain and HTTPS.
- On a bot plan the panel does: it installs
requirements.txtwhen it changes, starts the bot again after a crash, checks the token with Telegram before saving it, gives it to your code under five names, reads the console and names the cause when the bot stops answering (a refused token, a second copy, a webhook), with a button for the token and the webhook. Our bot plans do not include a public HTTPS address yet, so bots there use long polling, which is whatstart_pollingdoes.
See also How to use a MySQL database with an aiogram bot (aiomysql).
Questions people ask
Can I run an aiogram 2 bot?
Port it to aiogram 3 if you can: aiogram's migration guide lists the changes, starting with the removed Executor. The last aiogram 2 release, 2.25.2, came out on 6 October 2023. It lists Python 3.7 to 3.10 as supported and needs aiohttp older than 3.9, which PyPI offers as ready-built packages only up to Python 3.11. If you need the old code running before you port it, pin aiogram==2.25.2 in requirements.txt and set Runtime to Python 3.10, the newest version it lists.
Why does my aiogram bot look running but never answer?
aiogram keeps retrying once polling has started: on a token revoked in @BotFather, on another copy using the same token, and on a webhook left on the bot. Read the lines starting with Failed to fetch updates in Console; the panel turns each of the three into a banner that names the cause, with a button for the token and the webhook.
Where do aiogram's FSM states live?
With the default memory storage, in the bot process: they are lost when the bot restarts, for example after you change the code. To keep them, use a storage outside the process, such as aiogram's Redis storage with a Redis server you can reach.
Do I need a webhook to keep an aiogram bot online?
No. start_polling keeps asking Telegram for new messages, so the bot answers as long as the process runs. Moving a bot that used a webhook: How to move a Telegram bot to PlayNHost.
aiogram runs on every PlayNHost bot plan, from STARTER (0.5 GB) up. Compare the plans and click Create Server on Telegram bot hosting.



