MHOSTMHOST

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 like 123456789: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_IP

apt update && apt upgrade -y
apt install -y python3 python3-venv python3-pip git

adduser --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 - bot
git clone https://github.com/YOUR_ACCOUNT/YOUR_BOT.git mybot
cd 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 venv
source venv/bin/activate
pip install aiogram python-dotenv
pip 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...' > .env
chmod 600 .env

A minimal bot.py to test the setup:

import asyncio
import os

from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.types import Message
from dotenv import load_dotenv

load_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 bot
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=bot
WorkingDirectory=/home/bot/mybot
ExecStart=/home/bot/mybot/venv/bin/python bot.py
Restart=always
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

What matters here:

  • ExecStart points to the Python inside the virtual environment, so the bot sees its installed libraries.
  • Restart=always and RestartSec=5 restart the bot 5 seconds after a crash.
  • PYTHONUNBUFFERED=1 writes logs immediately, without buffering.

Enable and start the service:

systemctl daemon-reload
systemctl enable --now mybot
systemctl 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/bash
set -e
cd /home/bot/mybot
sudo -u bot git pull
sudo -u bot ./venv/bin/pip install -r requirements.txt
systemctl restart mybot
systemctl 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 mybot stops the bot.
  • systemctl restart mybot restarts it.
  • journalctl -u mybot -n 100 shows 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:

  1. Point a domain or subdomain to your server's IP.
  2. Install Nginx and get a Let's Encrypt SSL certificate with Certbot.
  3. In Nginx, proxy a path such as /webhook to the bot's local port.
  4. In the bot code, replace start_polling with a web server and a set_webhook call pointing to https://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.py instead 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 sure ExecStart points 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 -I https://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 the bot user lacks permissions on the folder.
  • The token leaked. Send /revoke to @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.