Hosting a Telegram Bot on a VPS: A Complete Guide

Telegram bots are one of the most practical things you can deploy on a VPS. They’re lightweight, always-on services that need a stable connection, a public IP, and a process that doesn’t die when you close your terminal. A VPS checks all three boxes — and once you’ve done this once, you’ll wonder why you ever tried running a bot on your laptop.

This guide walks you through the entire process: from creating a bot to deploying it on a VPS with proper process management, HTTPS webhooks, and automatic restarts. We’ll use Python and the popular python-telegram-bot library, but the infrastructure concepts apply equally to Node.js, Go, or any other stack.


What You’ll Need

  • A VPS running Ubuntu 22.04 or 24.04 (any plan with 1 vCPU and 1Gb RAM is sufficient for most bots)
  • A domain name or a dedicated IP address
  • A Telegram account to create the bot
  • Basic familiarity with the Linux command line

Step 1: Create Your Bot with BotFather

Every Telegram bot starts with BotFather — Telegram’s official bot for creating and managing bots.

  1. Open Telegram and search for @BotFather
  2. Send /newbot
  3. Choose a display name (e.g., My Awesome Bot)
  4. Choose a username ending in bot (e.g., myawesomebot)
  5. BotFather will reply with your bot token — a string that looks like 7123456789:AAF...

Copy this token and keep it safe. Anyone with this token can control your bot.


Step 2: Connect to Your VPS

Connect via SSH:

ssh root@your-server-ip

It’s good practice to create a dedicated user for running your bot rather than using root:

adduser botuser
usermod -aG sudo botuser
su - botuser

Step 3: Install Python and Dependencies

Ubuntu 22.04 and 24.04 come with Python 3 pre-installed. Verify:

python3 --version

Install pip and venv:

sudo apt update
sudo apt install python3-pip python3-venv -y

Create a project directory and a virtual environment:

mkdir ~/mybot && cd ~/mybot
python3 -m venv venv
source venv/bin/activate

Install the Telegram bot library:

pip install python-telegram-bot

Step 4: Write Your Bot

Create the bot script:

nano bot.py

Here’s a minimal but functional bot that responds to /start and echoes any text message:

#python
import logging
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, filters, ContextTypes

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)

BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Hello! I'm running on a VPS 24/7.")

async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(update.message.text)

if __name__ == "__main__":
    app = ApplicationBuilder().token(BOT_TOKEN).build()
    app.add_handler(CommandHandler("start", start))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
    app.run_polling()

Replace YOUR_BOT_TOKEN_HERE with the token from BotFather.

Test it locally first:

python bot.py

Open Telegram, find your bot, and send /start. If it responds, the code works. Press Ctrl+C to stop it.


Step 5: Keep It Running with systemd

Running your bot manually in a terminal means it dies the moment you disconnect. The right solution is systemd — the process manager built into every modern Linux distribution. It will start your bot automatically on boot and restart it if it crashes.

Create a service file:

sudo nano /etc/systemd/system/mybot.service

Paste the following (adjust paths if your username isn’t botuser):

[Unit]
Description=Telegram Bot
After=network.target

[Service]
Type=simple
User=botuser
WorkingDirectory=/home/botuser/mybot
ExecStart=/home/botuser/mybot/venv/bin/python bot.py
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl daemon-reload
sudo systemctl enable mybot
sudo systemctl start mybot

Check that it’s running:

sudo systemctl status mybot

You should see active (running). Your bot now starts automatically on every reboot and restarts within 5 seconds if it crashes.

To view logs:

journalctl -u mybot -f

Step 6: Polling vs Webhooks — Which Should You Use?

Your bot can receive updates from Telegram in two ways:

Polling (what the example above uses): Your bot constantly asks Telegram “any new messages?” every few seconds. Simple to set up, no SSL required, works behind NAT. Fine for development and low-traffic bots.

Webhooks: Telegram pushes updates to your server instantly the moment a message arrives. Faster response times, more efficient, and scales better. Requires a public HTTPS endpoint — meaning you need a domain and an SSL certificate.

For a production bot that needs to feel responsive, webhooks are the right choice.


Step 7: Setting Up Webhooks with SSL (Production Setup)

7.1 Point a Domain to Your VPS

In your DNS settings, create an A record pointing your domain (or a subdomain like bot.yourdomain.com) to your VPS IP address. Allow a few minutes for propagation.

7.2 Install Nginx and Certbot

sudo apt install nginx certbot python3-certbot-nginx -y

7.3 Create an Nginx Config

sudo nano /etc/nginx/sites-available/mybot
server {
    listen 80;
    server_name bot.yourdomain.com;

    location /webhook {
        proxy_pass http://127.0.0.1:8443;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Enable the config:

sudo ln -s /etc/nginx/sites-available/mybot /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

7.4 Get an SSL Certificate

sudo certbot --nginx -d bot.yourdomain.com

Certbot will automatically update your Nginx config with HTTPS and set up auto-renewal.

7.5 Update Your Bot to Use Webhooks

Update bot.py:

#python
import logging
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, filters, ContextTypes

logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)

BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"
WEBHOOK_URL = "https://bot.yourdomain.com/webhook"

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Hello! Running on VPS with webhooks.")

async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(update.message.text)

if __name__ == "__main__":
    app = ApplicationBuilder().token(BOT_TOKEN).build()
    app.add_handler(CommandHandler("start", start))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
    app.run_webhook(
        listen="0.0.0.0",
        port=8443,
        webhook_url=WEBHOOK_URL
    )

Restart the service:

sudo systemctl restart mybot

Step 8: Store Secrets Properly

Hardcoding your bot token in bot.py is fine for testing but bad practice for anything you’ll commit to version control or share. Use environment variables instead.

Create a .env file:

nano /home/botuser/mybot/.env
BOT_TOKEN=7123456789:AAF...

Install python-dotenv:

pip install python-dotenv

Update bot.py:

#python
import os
from dotenv import load_dotenv

load_dotenv()
BOT_TOKEN = os.getenv("BOT_TOKEN")

Restrict permissions on the .env file:

chmod 600 /home/botuser/mybot/.env

Step 9: Basic Firewall Setup

Lock down your VPS to only allow the traffic you need:

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable

If you’re using polling (no Nginx), also allow the bot’s port:

sudo ufw allow 8443

Step 10: Monitoring and Updates

Check bot status at any time:

sudo systemctl status mybot

View live logs:

journalctl -u mybot -f

Deploy an update:

cd ~/mybot
# edit bot.py or pull from git
sudo systemctl restart mybot

Set up a Git-based workflow (optional but recommended): push your bot code to a private Git repository and pull updates to the VPS when you deploy. This gives you version history, easy rollbacks, and a clean separation between development and production.


Troubleshooting Common Issues

Bot doesn’t respond after deploy Check the logs: journalctl -u mybot -n 50. Most issues are import errors or a wrong bot token.

Webhook not receiving updates Verify the SSL certificate is valid, Nginx is running, and the webhook URL matches exactly what Telegram expects. You can check the current webhook status via the Telegram Bot API:

https://api.telegram.org/bot<TOKEN>/getWebhookInfo

Bot crashes on restart If the bot crashes immediately at start, systemd will retry every 5 seconds. Check logs to find the root cause before it fills your journal.

Port already in use If you see Address already in use, another process is on port 8443. Find it with sudo lsof -i :8443 and stop it.


Scaling Up

A basic bot on a 1 vCPU / 1Gb VPS will handle thousands of users without breaking a sweat. But if your bot grows — adding databases, image processing, external API calls, or multiple concurrent workflows — you’ll want to think about:

  • A database: PostgreSQL or SQLite for storing user state, conversation history, or application data
  • Redis: For job queues, rate limiting, and caching
  • More RAM: Especially if you’re running multiple bots or heavy dependencies on the same server
  • Multiple bots on one VPS: Each gets its own systemd service and Nginx location block — straightforward to manage

Hosting.international’s KVM VPS plans let you scale CPU and RAM without migrating to a new server, so you can start small and grow as your bot’s user base does.


Summary

StepWhat you did
1Created a bot token with BotFather
2Connected to VPS via SSH
3Set up Python and a virtual environment
4Wrote and tested the bot locally
5Configured systemd for always-on operation
6Chose between polling and webhooks
7Set up Nginx + SSL for webhook mode
8Stored secrets in environment variables
9Configured UFW firewall
10Established a deployment and monitoring workflow

A VPS is the cleanest way to run a Telegram bot in production. You get a stable IP, 24/7 uptime, full control over the environment, and none of the cold-start latency of serverless platforms.

Get a VPS for your Telegram bot →