How to Run a Telegram Bot on a VPS 24/7: A Step-by-Step Guide
To keep a Telegram bot responding around the clock, it has to run on a server that never shuts down. The simplest and cheapest option is a VPS with the bot started automatically by systemd.
On a home computer, the bot stops as soon as you close the laptop or lose your connection. Free platforms put idle apps to sleep and cap running hours. On a VPS, the bot runs nonstop, restarts itself after a crash and starts again after a server reboot.
This guide shows how to run a Python bot (aiogram 3) on Ubuntu 22.04/24.04. For Node.js bots the steps are the same; only the start command differs.
What You Need
- A VPS. 1 vCPU and 1 GB of RAM is enough for a typical bot. If your bot uses a database, processes files or serves thousands of users, get 2 GB of RAM or more.
- OS: Ubuntu 22.04 or 24.04.
- A bot token. Message @BotFather in Telegram, send
/newbot, and set a name and username. BotFather will send a token like123456789:AAH.... - SSH access to the server. Your host provides the login and password or key after purchase.
Server location matters. Choose a country where Telegram isn't blocked, or the bot won't be able to reach the API.
Step 1. Prepare the Server
Connect to the VPS, update the system and create a separate user for the bot. Running the bot as root is unsafe: a vulnerability in the code would give an attacker full access to the server.
ssh root@SERVER_IPapt update && apt upgrade -yapt install -y python3 python3-venv python3-pip gitadduser --disabled-password --gecos "" bot
Check the Python version with python3 --version. aiogram 3 needs Python 3.9 or newer, which Ubuntu 22.04 and 24.04 already have.
Step 2. Upload the Bot Code and Install Dependencies
Switch to the bot user and move your code to the server. Git is the easiest way:
su - botgit clone https://github.com/YOUR_ACCOUNT/YOUR_BOT.git mybotcd mybot
No repository? Copy the files from your computer: scp -r ./mybot bot@SERVER_IP:~/.
Create a virtual environment and install the libraries. The environment isolates the bot's dependencies from the system Python:
python3 -m venv venvsource venv/bin/activatepip install aiogram python-dotenvpip freeze > requirements.txt
Keep the token in a .env file, not in the code. Otherwise it ends up in Git, and anyone who sees the repository can control your bot.
echo 'BOT_TOKEN=123456789:AAH...' > .envchmod 600 .env
A minimal bot.py to test the setup:
import asyncioimport osfrom aiogram import Bot, Dispatcherfrom aiogram.filters import CommandStartfrom aiogram.types import Messagefrom dotenv import load_dotenvload_dotenv()bot = Bot(token=os.getenv("BOT_TOKEN"))dp = Dispatcher()@dp.message(CommandStart())async def start(message: Message): await message.answer("The bot is running on a VPS 24/7!")@dp.message()async def echo(message: Message): await message.send_copy(chat_id=message.chat.id)async def main(): await dp.start_polling(bot)if __name__ == "__main__": asyncio.run(main())
Test it manually with python bot.py. Send /start to the bot and it should reply. Stop it with Ctrl+C and leave the user session with exit.
Step 3. Run the Bot 24/7 with systemd
If you start the bot with python bot.py, it stops when you close the SSH session. systemd runs the bot as a service: in the background, restarting it automatically after a crash and after a server reboot.
Create the service file as root:
nano /etc/systemd/system/mybot.service
[Unit]Description=Telegram botAfter=network-online.targetWants=network-online.target[Service]Type=simpleUser=botWorkingDirectory=/home/bot/mybotExecStart=/home/bot/mybot/venv/bin/python bot.pyRestart=alwaysRestartSec=5Environment=PYTHONUNBUFFERED=1
[Install]WantedBy=multi-user.target
What matters here:
ExecStartpoints to the Python inside the virtual environment, so the bot sees its installed libraries.Restart=alwaysandRestartSec=5restart the bot 5 seconds after a crash.PYTHONUNBUFFERED=1writes logs immediately, without buffering.
Enable and start the service:
systemctl daemon-reload
systemctl enable --now mybotsystemctl status mybot
The active (running) status means the bot is up. To follow the logs live: journalctl -u mybot -f.
For a Node.js bot, change the start line to ExecStart=/usr/bin/node bot.js. Run which node to find the path to Node.js.
Step 4. Update the Bot with One Command
After changing the code, you need to pull the new version, update dependencies and restart the service. Put it all in a script, /home/bot/mybot/deploy.sh:
#!/bin/bashset -ecd /home/bot/mybotsudo -u bot git pullsudo -u bot ./venv/bin/pip install -r requirements.txtsystemctl restart mybotsystemctl status mybot --no-pager
Make the script executable and run it as root:
chmod +x /home/bot/mybot/deploy.sh/home/bot/mybot/deploy.sh
Other useful commands:
systemctl stop mybotstops the bot.systemctl restart mybotrestarts it.journalctl -u mybot -n 100shows the last 100 log lines.
Polling or Webhook: Which to Choose
Polling, which we set up above, is enough for most bots. You need a webhook when you have many users and response speed matters.
- Polling. The bot regularly asks Telegram for new messages. No domain, SSL or open ports needed. Good for personal bots and bots with up to a few thousand users.
- Webhook. Telegram pushes messages to your server. Responses are faster and load is lower, but you need a domain with HTTPS. Telegram only sends webhooks to ports 443, 80, 88 and 8443.
To switch to a webhook:
- Point a domain or subdomain to your server's IP.
- Install Nginx and get a Let's Encrypt SSL certificate with Certbot.
- In Nginx, proxy a path such as
/webhookto the bot's local port. - In the bot code, replace
start_pollingwith a web server and aset_webhookcall pointing tohttps://your-domain/webhook.
aiogram 3 has a ready-made module for this, aiogram.webhook.aiohttp_server, with an example in the library's documentation.
Common Errors and How to Fix Them
- The bot stops after you close SSH. It was started with
python bot.pyinstead of as a service. Run it through systemd as in Step 3. ModuleNotFoundError: No module named 'aiogram'. The service uses the system Python instead of the virtual environment. Make sureExecStartpoints to.../venv/bin/python.TelegramConflictError: terminated by other getUpdates request. The bot is running twice, for example on the server and on your computer, or manually alongside the service. Keep only one instance.- The bot doesn't reply and the logs show timeouts. The server can't reach the Telegram API. Check with
curl -Ihttps://api.telegram.org. If there's no connection, choose a VPS in another location. - The service won't start. Find the reason in the logs:
journalctl -u mybot -n 50. Usually it's a typo in a path or thebotuser lacks permissions on the folder. - The token leaked. Send
/revoketo @BotFather, get a new token and replace it in.env.
Checklist
FAQ
How many resources does a Telegram bot need? A simple bot runs fine on 1 vCPU and 1 GB of RAM, and one such VPS can host several bots. Bots with databases, media processing or AI models need more.
Can I run several bots on one VPS? Yes. Give each bot its own folder, virtual environment and systemd service: bot1.service, bot2.service and so on.
Which is better: systemd, screen, pm2 or Docker? systemd comes with Ubuntu and restarts the bot after crashes and reboots; screen can't do that. pm2 is handy for Node.js, and Docker makes sense when the bot has several services, such as a bot plus a database.
Do I need a domain for my bot? Not for polling. You only need a domain with HTTPS for a webhook.
How do I check that the bot survives a server reboot? Run reboot, wait a minute and message the bot. If it replies, autostart works.