DEV Community

Buran Host
Buran Host

Posted on Originally published at Medium

Run a Telegram Bot 24/7 on a VPS: Python, systemd and Auto-Restart

Disclosure: I run Buran.host, a small VPS reseller, so that is the example I use. The steps work on any Ubuntu 22.04 or 24.04 VPS.

A Telegram bot that lives on your laptop dies when the laptop sleeps. A small VPS fixes that: it is always on, it has a stable IP, and systemd restarts your bot if it crashes or the server reboots. Here is the whole setup, start to finish.

What you need

  • A VPS with Ubuntu 22.04 or 24.04 (a bot needs very little, 1 GB of RAM is plenty for most bots)
  • SSH access
  • A bot token from @botfather## 1. Create a user for the bot

Do not run the bot as root. If the bot has a bug, you want it to be able to damage as little as possible.

adduser --disabled-password --gecos "" botuser
Enter fullscreen mode Exit fullscreen mode

2. Install Python and create a virtual environment

apt update && apt install -y python3-venv python3-pip git
sudo -u botuser python3 -m venv /home/botuser/venv
Enter fullscreen mode Exit fullscreen mode

Copy your bot code to /home/botuser/bot/ (git clone or scp), then install the dependencies:

sudo -u botuser /home/botuser/venv/bin/pip install python-telegram-bot
Enter fullscreen mode Exit fullscreen mode

Use whatever library your bot already uses. The systemd part is the same for aiogram, telebot or anything else.

3. Keep the token out of the code

Put the token in an environment file that only the bot user can read:

echo 'BOT_TOKEN=123456:your-token-here' > /home/botuser/bot.env
chown botuser:botuser /home/botuser/bot.env
chmod 600 /home/botuser/bot.env
Enter fullscreen mode Exit fullscreen mode

In Python read it with os.environ["BOT_TOKEN"]. Never commit the token to git. If it leaks, revoke it in @botfather with /revoke.

4. Write the systemd unit

Create /etc/systemd/system/tgbot.service:

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

[Service]
User=botuser
WorkingDirectory=/home/botuser/bot
EnvironmentFile=/home/botuser/bot.env
ExecStart=/home/botuser/venv/bin/python main.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode

The two lines that matter are Restart=always and RestartSec=5: if the process dies for any reason, systemd starts it again after five seconds.

5. Start it and enable it on boot

systemctl daemon-reload
systemctl enable --now tgbot
systemctl status tgbot
Enter fullscreen mode Exit fullscreen mode

enable makes the bot start after a reboot. Send your bot a message in Telegram to confirm it answers.

6. Read the logs

journalctl -u tgbot -f
Enter fullscreen mode Exit fullscreen mode

This follows the bot output live. Python errors, restarts and crashes all show up here, so it is the first place to look when something goes wrong.

7. Updating the bot

cd /home/botuser/bot && sudo -u botuser git pull
systemctl restart tgbot
Enter fullscreen mode Exit fullscreen mode

Polling or webhook?

For most small bots, long polling is the simplest choice: no domain, no HTTPS certificate, nothing exposed to the internet. Webhooks make sense when you have many users and want Telegram to push updates to you. Then you need a domain and HTTPS, for example with Caddy in front of the bot.

A few mistakes to avoid

  • Running as root. Use a separate user, as above.
  • Hard-coding the token. Use the env file.
  • No restart policy. Without Restart=always one unhandled exception ends your bot until you notice.
  • Running two copies of the same bot. Telegram returns a "conflict" error if two processes poll with the same token. Stop the old one first.

Want a longer version?

I wrote a step-by-step guide with the same commands here: https://buran.host/guides/deploy-telegram-bot

If you need a server for it, plans start at €12/month with a dedicated IPv4 address: https://buran.host/vps

Top comments (0)