Telegram Bot on VPS Server: Complete Setup Guide
Introduction
Telegram bots have revolutionized how businesses, communities, and individuals automate tasks, deliver services, and interact with users on one of the world's fastest-growing messaging platforms. With over 700 million active users, Telegram provides a powerful bot API enabling developers to create automated assistants, notification systems, customer service tools, game bots, productivity helpers, and countless other applications.
Unlike traditional web applications requiring complex user interfaces, Telegram bots leverage the familiar messaging interface users already know, reducing adoption friction while providing rich functionality through inline keyboards, custom commands, file sharing, and media support. Running bots on VPS servers ensures 24/7 availability, scalability, and independence from local infrastructure.
This comprehensive guide walks you through deploying production-ready Telegram bots on a VPS server. You'll learn bot creation and registration, programming language options (Python, Node.js, PHP), webhook and long-polling configurations, database integration, security best practices, process management, monitoring, and deployment automation.
Whether building a customer support bot, notification system, community management tool, e-commerce assistant, automated content delivery service, or interactive game, this guide provides everything needed to successfully deploy and maintain Telegram bots on VPS infrastructure.
Use Case Overview
Why Deploy a Telegram Bot?
Telegram bots offer unique advantages for various applications:
Automated Customer Support: Provide instant responses to frequently asked questions, ticket creation, order status checks, and basic troubleshooting without human intervention.
Notification and Alerts: Send real-time notifications for system events, monitoring alerts, order confirmations, appointment reminders, or any time-sensitive information directly to users.
Content Delivery: Automatically distribute news updates, blog posts, product announcements, or educational content to subscribers on a schedule or triggered by events.
Data Collection: Gather user feedback, survey responses, registration information, or form submissions through conversational interfaces without building separate forms.
E-commerce Integration: Enable product browsing, order placement, payment processing, and customer service entirely within Telegram.
Community Management: Moderate groups, welcome new members, enforce rules, manage permissions, and track activity in Telegram communities.
Productivity Tools: Task management, reminders, note-taking, file storage, translation services, or calculations accessible from any device with Telegram.
Gaming and Entertainment: Interactive games, trivia contests, fortune tellers, music players, or other entertainment features engaging users through chat.
API Interfaces: Provide user-friendly chat interfaces for complex APIs, databases, or services, making technical systems accessible to non-technical users.
Common Bot Types
Command Bots: Respond to specific commands (/start, /help, /status) with predefined responses or actions.
Conversational Bots: Natural language processing bots engaging in contextual conversations using AI or decision trees.
Inline Bots: Triggered by typing @botusername in any chat, providing search results, media, or quick actions without leaving the conversation.
Payment Bots: Process payments for products or services using Telegram's integrated payment system.
Game Bots: Interactive games ranging from simple trivia to complex multiplayer experiences.
Admin Bots: Group management bots handling moderation, welcoming, user verification, and administrative tasks.
Webhook Bots: Receive notifications from external services (GitHub, monitoring systems, CI/CD pipelines) and forward to Telegram.
Technology Stack Options
Python:
- Libraries: python-telegram-bot, aiogram, pyrogram
- Advantages: Extensive bot libraries, machine learning integration, beginner-friendly
- Use Cases: General purpose, AI bots, data science applications
Node.js:
- Libraries: node-telegram-bot-api, telegraf, Grammy
- Advantages: Fast, async processing, JavaScript ecosystem
- Use Cases: Real-time applications, webhook integrations, full-stack projects
PHP:
- Libraries: telegram-bot-sdk, TelegramBot
- Advantages: Simple deployment, web hosting compatibility
- Use Cases: Simple bots, web integration, legacy system integration
Go:
- Libraries: telebot, tgbotapi
- Advantages: High performance, compiled binaries, low resource usage
- Use Cases: High-traffic bots, system automation
Requirements
System Requirements
Minimum Requirements (Simple Bot, < 1000 users):
- CPU: 1 core at 1.5+ GHz
- RAM: 512MB
- Storage: 10GB
- Network: 1 Mbps
- OS: Ubuntu 20.04/22.04, Debian 11/12
Recommended Requirements (Medium Bot, 1000-10000 users):
- CPU: 2 cores at 2.0+ GHz
- RAM: 2GB
- Storage: 20GB SSD
- Network: 10 Mbps
- OS: Ubuntu 22.04 LTS
High-Performance Requirements (Large Bot, 10000+ users):
- CPU: 4+ cores at 2.5+ GHz
- RAM: 8GB+
- Storage: 50GB SSD
- Network: 100 Mbps
- OS: Ubuntu 22.04 LTS
Software Requirements
Core Components:
- Programming language runtime (Python 3.9+, Node.js 16+, PHP 8+)
- Process manager (systemd, PM2, or supervisord)
- Database (SQLite, MySQL, PostgreSQL, MongoDB)
- Optional: Redis for caching and session storage
Development Tools:
- Git for version control
- Text editor or IDE
- SSH client for server access
Telegram Requirements
Bot Token: Obtained from @BotFather on Telegram.
Telegram Account: Valid Telegram account to create and manage bots.
Domain (Optional): For webhook mode, HTTPS domain with valid SSL certificate.
Prerequisites Knowledge
- Basic programming in chosen language
- Linux command-line fundamentals
- Basic understanding of HTTP/HTTPS and APIs
- Git version control basics
- Telegram app usage
Step-by-Step Setup
Step 1: Create Telegram Bot
Open Telegram and find @BotFather.
Start conversation and create new bot:
/newbot
Follow prompts:
- Enter bot name (e.g., "My Awesome Bot")
- Enter bot username (must end in 'bot', e.g., "my_awesome_bot")
@BotFather provides bot token:
1234567890:ABCdefGHIjklMNOpqrsTUVwxyz-ABCDEFG
Save this token securely - it's your bot's authentication key.
Configure bot settings:
/setdescription - Set bot description
/setabouttext - Set about text
/setuserpic - Upload bot profile picture
/setcommands - Set command list
Example command list:
start - Start the bot
help - Show help message
status - Check bot status
settings - Configure settings
Step 2: Prepare VPS Server
Update system:
sudo apt update && sudo apt upgrade -y
Install essential tools:
sudo apt install -y git curl wget build-essential
Step 3: Install Python Bot (Example)
Install Python and pip:
sudo apt install -y python3 python3-pip python3-venv
Create project directory:
mkdir -p ~/telegram-bots/mybot
cd ~/telegram-bots/mybot
Create virtual environment:
python3 -m venv venv
source venv/bin/activate
Install python-telegram-bot:
pip install python-telegram-bot python-dotenv
Step 4: Create Simple Bot
Create bot script:
nano bot.py
Add basic bot code:
#!/usr/bin/env python3
import os
import logging
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
BOT_TOKEN = os.getenv('BOT_TOKEN')
# Enable logging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO
)
logger = logging.getLogger(__name__)
# Command handlers
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Send a message when the command /start is issued."""
user = update.effective_user
await update.message.reply_text(
f'Hi {user.first_name}! Welcome to My Awesome Bot.\n\n'
f'Use /help to see available commands.'
)
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Send a message when the command /help is issued."""
help_text = """
Available commands:
/start - Start the bot
/help - Show this help message
/status - Check bot status
/echo <text> - Echo your message
Send me any message and I'll echo it back!
"""
await update.message.reply_text(help_text)
async def status(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Send bot status when /status is issued."""
await update.message.reply_text('Bot is running! 🤖 All systems operational.')
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Echo the user message."""
await update.message.reply_text(update.message.text)
async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Log errors caused by Updates."""
logger.error(f'Update {update} caused error {context.error}')
def main():
"""Start the bot."""
# Create the Application
application = Application.builder().token(BOT_TOKEN).build()
# Register command handlers
application.add_handler(CommandHandler("start", start))
application.add_handler(CommandHandler("help", help_command))
application.add_handler(CommandHandler("status", status))
# Register message handler for echoing
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
# Register error handler
application.add_error_handler(error_handler)
# Start the Bot
logger.info("Bot started - polling for updates")
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == '__main__':
main()
Create environment file:
nano .env
Add:
BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz-ABCDEFG
Make bot executable:
chmod +x bot.py
Step 5: Test Bot Locally
Run bot:
python bot.py
Open Telegram, find your bot, and test:
/start
/help
/status
Hello bot!
Bot should respond to commands and echo messages.
Stop bot with Ctrl+C.
Step 6: Create Systemd Service
Deactivate virtual environment:
deactivate
Create systemd service:
sudo nano /etc/systemd/system/telegram-bot.service
Add:
[Unit]
Description=Telegram Bot
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/telegram-bots/mybot
Environment="PATH=/home/ubuntu/telegram-bots/mybot/venv/bin"
ExecStart=/home/ubuntu/telegram-bots/mybot/venv/bin/python bot.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Adjust paths and user as needed.
Enable and start service:
sudo systemctl daemon-reload
sudo systemctl enable telegram-bot
sudo systemctl start telegram-bot
Check status:
sudo systemctl status telegram-bot
View logs:
sudo journalctl -u telegram-bot -f
Configuration
Database Integration
Install database libraries:
pip install sqlalchemy psycopg2-binary
Create database models:
from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from datetime import datetime
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
telegram_id = Column(Integer, unique=True)
username = Column(String)
first_name = Column(String)
created_at = Column(DateTime, default=datetime.utcnow)
# Database setup
engine = create_engine('sqlite:///bot.db')
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
Save user on /start:
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
user = update.effective_user
# Save to database
session = Session()
db_user = session.query(User).filter_by(telegram_id=user.id).first()
if not db_user:
db_user = User(
telegram_id=user.id,
username=user.username,
first_name=user.first_name
)
session.add(db_user)
session.commit()
session.close()
await update.message.reply_text(f'Welcome {user.first_name}!')
Webhook Configuration
Webhook mode is more efficient for high-traffic bots.
Requirements:
- HTTPS domain with valid SSL certificate
- Public IP or domain pointing to server
Setup Nginx reverse proxy:
sudo apt install nginx certbot python3-certbot-nginx -y
Obtain SSL certificate:
sudo certbot --nginx -d bot.example.com
Configure Nginx:
sudo nano /etc/nginx/sites-available/telegram-bot
Add:
server {
listen 443 ssl http2;
server_name bot.example.com;
ssl_certificate /etc/letsencrypt/live/bot.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bot.example.com/privkey.pem;
location /webhook {
proxy_pass http://localhost:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Enable site:
sudo ln -s /etc/nginx/sites-available/telegram-bot /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Update bot code for webhook:
def main():
application = Application.builder().token(BOT_TOKEN).build()
# Register handlers
application.add_handler(CommandHandler("start", start))
# ... other handlers
# Webhook configuration
application.run_webhook(
listen="127.0.0.1",
port=8443,
url_path="webhook",
webhook_url=f"https://bot.example.com/webhook"
)
Inline Keyboard Buttons
Add interactive buttons:
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
async def menu(update: Update, context: ContextTypes.DEFAULT_TYPE):
keyboard = [
[InlineKeyboardButton("Option 1", callback_data='1')],
[InlineKeyboardButton("Option 2", callback_data='2')],
[InlineKeyboardButton("Help", callback_data='help')]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text('Choose an option:', reply_markup=reply_markup)
async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
await query.answer()
if query.data == '1':
await query.edit_message_text('You selected Option 1!')
elif query.data == '2':
await query.edit_message_text('You selected Option 2!')
elif query.data == 'help':
await query.edit_message_text('Help information here')
# Register handlers
application.add_handler(CommandHandler('menu', menu))
application.add_handler(CallbackQueryHandler(button_handler))
File Handling
Handle file uploads:
async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
photo_file = await update.message.photo[-1].get_file()
await photo_file.download_to_drive(f'downloads/{photo_file.file_id}.jpg')
await update.message.reply_text('Photo received and saved!')
async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
document = update.message.document
doc_file = await document.get_file()
await doc_file.download_to_drive(f'downloads/{document.file_name}')
await update.message.reply_text(f'File {document.file_name} received!')
# Register handlers
application.add_handler(MessageHandler(filters.PHOTO, handle_photo))
application.add_handler(MessageHandler(filters.Document.ALL, handle_document))
Send files to users:
async def send_file(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_document(document=open('report.pdf', 'rb'))
await update.message.reply_photo(photo=open('image.jpg', 'rb'))
Scheduled Tasks
Send periodic messages:
from telegram.ext import JobQueue
from datetime import time
async def daily_reminder(context: ContextTypes.DEFAULT_TYPE):
chat_id = context.job.data
await context.bot.send_message(
chat_id=chat_id,
text="Daily reminder: Don't forget to check your tasks!"
)
async def set_reminder(update: Update, context: ContextTypes.DEFAULT_TYPE):
chat_id = update.effective_chat.id
# Schedule daily reminder at 9:00 AM
context.job_queue.run_daily(
daily_reminder,
time=time(hour=9, minute=0),
data=chat_id
)
await update.message.reply_text('Daily reminder set for 9:00 AM!')
application.add_handler(CommandHandler('remind', set_reminder))
Admin Commands
Restrict commands to specific users:
ADMIN_IDS = [123456789, 987654321] # Your Telegram user IDs
def admin_only(func):
async def wrapper(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
if user_id not in ADMIN_IDS:
await update.message.reply_text('This command is for administrators only.')
return
return await func(update, context)
return wrapper
@admin_only
async def broadcast(update: Update, context: ContextTypes.DEFAULT_TYPE):
message = ' '.join(context.args)
session = Session()
users = session.query(User).all()
for user in users:
try:
await context.bot.send_message(chat_id=user.telegram_id, text=message)
except Exception as e:
logger.error(f'Failed to send to {user.telegram_id}: {e}')
await update.message.reply_text(f'Broadcast sent to {len(users)} users!')
application.add_handler(CommandHandler('broadcast', broadcast))
Monitoring and Maintenance
Logging Configuration
Enhanced logging:
import logging
from logging.handlers import RotatingFileHandler
# Create logs directory
os.makedirs('logs', exist_ok=True)
# Configure logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# File handler with rotation
file_handler = RotatingFileHandler(
'logs/bot.log',
maxBytes=10485760, # 10MB
backupCount=5
)
file_handler.setFormatter(
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
)
# Console handler
console_handler = logging.StreamHandler()
console_handler.setFormatter(
logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
)
logger.addHandler(file_handler)
logger.addHandler(console_handler)
Health Check Endpoint
Add health check:
from flask import Flask
import threading
app = Flask(__name__)
@app.route('/health')
def health():
return {'status': 'ok', 'bot': 'running'}, 200
def run_flask():
app.run(host='127.0.0.1', port=5000)
# Start Flask in background thread
threading.Thread(target=run_flask, daemon=True).start()
Bot Statistics
Track usage:
from collections import defaultdict
import json
stats = defaultdict(int)
async def track_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
stats['total_messages'] += 1
stats[f'user_{update.effective_user.id}'] += 1
# Save stats periodically
with open('stats.json', 'w') as f:
json.dump(dict(stats), f)
async def get_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
if user_id not in ADMIN_IDS:
return
total = stats['total_messages']
unique_users = len([k for k in stats.keys() if k.startswith('user_')])
await update.message.reply_text(
f'📊 Bot Statistics:\n\n'
f'Total messages: {total}\n'
f'Unique users: {unique_users}'
)
Error Notifications
Send errors to admin:
async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
logger.error(f'Update {update} caused error {context.error}')
# Notify admin
error_message = f'⚠️ Error occurred:\n\n{context.error}'
for admin_id in ADMIN_IDS:
try:
await context.bot.send_message(chat_id=admin_id, text=error_message)
except Exception as e:
logger.error(f'Failed to notify admin: {e}')
Security Best Practices
Environment Variables
Never hardcode sensitive data:
nano .env
BOT_TOKEN=your_bot_token_here
DATABASE_URL=postgresql://user:pass@localhost/botdb
ADMIN_IDS=123456789,987654321
Load in bot:
from dotenv import load_dotenv
load_dotenv()
BOT_TOKEN = os.getenv('BOT_TOKEN')
ADMIN_IDS = [int(id) for id in os.getenv('ADMIN_IDS').split(',')]
Add .env to .gitignore:
echo ".env" >> .gitignore
Input Validation
Validate user input:
async def process_age(update: Update, context: ContextTypes.DEFAULT_TYPE):
try:
age = int(context.args[0])
if age < 0 or age > 120:
raise ValueError("Invalid age range")
await update.message.reply_text(f'Age {age} recorded!')
except (IndexError, ValueError):
await update.message.reply_text('Please provide a valid age: /age <number>')
Rate Limiting
Prevent spam:
from collections import defaultdict
from datetime import datetime, timedelta
user_last_message = defaultdict(lambda: datetime.min)
async def rate_limit(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
now = datetime.now()
if now - user_last_message[user_id] < timedelta(seconds=2):
await update.message.reply_text('Please slow down!')
return False
user_last_message[user_id] = now
return True
Secure File Handling
Validate uploaded files:
ALLOWED_EXTENSIONS = {'.pdf', '.jpg', '.png', '.docx'}
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
async def handle_document(update: Update, context: ContextTypes.DEFAULT_TYPE):
document = update.message.document
# Check file size
if document.file_size > MAX_FILE_SIZE:
await update.message.reply_text('File too large! Maximum 10MB.')
return
# Check extension
file_ext = os.path.splitext(document.file_name)[1].lower()
if file_ext not in ALLOWED_EXTENSIONS:
await update.message.reply_text(f'File type not allowed. Allowed: {ALLOWED_EXTENSIONS}')
return
# Process file
doc_file = await document.get_file()
await doc_file.download_to_drive(f'uploads/{document.file_id}{file_ext}')
await update.message.reply_text('File uploaded successfully!')
Troubleshooting
Bot Not Responding
Check service status:
sudo systemctl status telegram-bot
View logs:
sudo journalctl -u telegram-bot -n 100
Test bot token:
curl https://api.telegram.org/bot<YOUR_TOKEN>/getMe
Should return bot information.
Verify network connectivity:
ping api.telegram.org
High Memory Usage
Monitor memory:
htop
Optimize database queries:
# Use connection pooling
engine = create_engine('postgresql://...', pool_size=10, max_overflow=20)
# Close sessions
session.close()
Limit concurrent updates:
application = Application.builder().token(BOT_TOKEN).concurrent_updates(True).build()
Webhook Not Working
Check SSL certificate:
sudo certbot certificates
Test webhook URL:
curl https://bot.example.com/webhook
Verify webhook is set:
curl https://api.telegram.org/bot<TOKEN>/getWebhookInfo
Delete and reset webhook:
curl https://api.telegram.org/bot<TOKEN>/deleteWebhook
Conclusion
You now have a production-ready Telegram bot running on a VPS server with professional deployment, monitoring, and security practices. This foundation supports building sophisticated bot applications serving thousands of users reliably.
Key achievements:
- 24/7 bot availability through systemd service management
- Database integration for persistent user data and state
- Interactive features with inline keyboards and file handling
- Security implementation with input validation and rate limiting
- Professional monitoring with logging and statistics
- Scalable architecture ready for webhook mode and high traffic
Regular maintenance includes monitoring logs, updating dependencies, backing up databases, and improving bot features based on user feedback.
Build amazing bots!


