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
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
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
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
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
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
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
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
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=alwaysone 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)