
Bạn vừa viết xong bot Telegram/Discord chạy ngon trên laptop, nhưng tắt máy là bot chết. Đây là tutorial deploy chuẩn lên VPS Ubuntu chạy 24/7: Python aiogram 3.x cho Telegram, Node.js discord.js v14 cho Discord, systemd service để auto-restart khi crash hoặc khi VPS reboot, log với journalctl, monitoring uptime gửi alert Telegram. Bot nhẹ chạy ngon trên VPS 20 (199k/tháng) - đủ cho ~10-50k user concurrent. Setup 30 phút, dùng nhiều năm không phải lo.
1. Cấu hình VPS theo loại bot
| Loại bot | vCPU | RAM | Gợi ý gói |
|---|---|---|---|
| Bot Telegram đơn giản (reply, command, <1000 user/ngày) | 2 | 2GB | VPS 20 (199k) |
| Bot có DB, queue, vài chục API call/giây | 2 | 2GB | VPS 20 (199k) |
| Bot Discord nhiều server, cache message | 2 | 4GB | VPS 30 (299k) |
| Bot AI (gọi LLM, cần concurrent), bot scrape | 4 | 4GB | VPS 30 (299k) |
| Multi-bot platform (chạy 10+ bot cùng VPS) | 4 | 8GB | VPS 50 (639k) |
Với bot đơn giản hoặc mid-traffic, VPS 20 của TND giá 199.000đ/tháng (2 vCPU, 2GB RAM, 40GB Ceph SSD NVMe) là đủ. Có IPv6 + IPv4 sạch, khởi tạo 60s, OS chọn Ubuntu 24.04 LTS.
2. Setup VPS Ubuntu cơ bản
# SSH vào VPS lần đầu
ssh root@your-vps-ip
# Tạo user riêng cho bot (không chạy bot bằng root)
adduser botuser
usermod -aG sudo botuser
# Copy SSH key
mkdir -p /home/botuser/.ssh
cp ~/.ssh/authorized_keys /home/botuser/.ssh/
chown -R botuser:botuser /home/botuser/.ssh
chmod 700 /home/botuser/.ssh
chmod 600 /home/botuser/.ssh/authorized_keys
# Update OS, cài dependency cơ bản
apt update && apt upgrade -y
apt install -y git curl wget htop ufw fail2ban tmux
# Firewall: chỉ cho SSH
ufw allow OpenSSH
ufw enable
# Disable root SSH login (security)
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
systemctl restart sshd
# Logout root, SSH lại bằng botuser
exit
ssh botuser@your-vps-ip3. Telegram bot với Python aiogram 3.x
Tạo bot trên Telegram
- Mở Telegram, search @BotFather.
- Gửi
/newbot, đặt tên + username (phải kết thúc bằng "bot"). - BotFather trả về token:
1234567890:ABCdefGHIjklMNOpqrsTUVwxyz. Lưu lại.
Setup Python project
# Cài Python 3.11+ và uv (package manager nhanh, 2026 best practice)
sudo apt install -y python3 python3-venv python3-pip
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
# Tạo project folder
mkdir -p ~/bots/tele-bot && cd ~/bots/tele-bot
# Tạo virtual env + install aiogram
uv venv
source .venv/bin/activate
uv pip install aiogram==3.28.2 python-dotenv aiohttp
# Tạo file .env (không commit lên git)
cat > .env << 'EOF'
BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
ADMIN_ID=123456789
EOF
chmod 600 .envCode bot mẫu
File: ~/bots/tele-bot/bot.py
import asyncio
import logging
import os
from dotenv import load_dotenv
from aiogram import Bot, Dispatcher, types, F
from aiogram.filters import Command
from aiogram.types import Message
load_dotenv()
BOT_TOKEN = os.getenv("BOT_TOKEN")
ADMIN_ID = int(os.getenv("ADMIN_ID", "0"))
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
@dp.message(Command("start"))
async def cmd_start(message: Message):
await message.answer(
f"Xin chào {message.from_user.full_name}! Bot 24/7 đang chạy.\n"
"Gõ /help để xem lệnh."
)
@dp.message(Command("help"))
async def cmd_help(message: Message):
await message.answer(
"Lệnh:\n"
"/start - bắt đầu\n"
"/help - trợ giúp\n"
"/ping - check bot còn sống"
)
@dp.message(Command("ping"))
async def cmd_ping(message: Message):
await message.answer("pong! Bot alive.")
@dp.message(F.text)
async def echo(message: Message):
await message.answer(f"Bạn vừa nói: {message.text}")
async def on_startup():
logger.info("Bot started")
if ADMIN_ID:
try:
await bot.send_message(ADMIN_ID, "🟢 Bot online")
except Exception as e:
logger.warning(f"Cannot notify admin: {e}")
async def main():
dp.startup.register(on_startup)
await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types())
if __name__ == "__main__":
try:
asyncio.run(main())
except (KeyboardInterrupt, SystemExit):
logger.info("Bot stopped")Test chạy:
cd ~/bots/tele-bot
source .venv/bin/activate
python bot.py
# Mở Telegram, search bot username, /start
# Nếu reply là OK. Ctrl+C dừng.4. Discord bot với Node.js discord.js v14
Tạo bot trên Discord
- Vào
discord.com/developers/applications, New Application. - Tab Bot -> Add Bot. Lưu token (chỉ hiện 1 lần).
- Tab Bot -> bật Message Content Intent (nếu bot đọc message).
- Tab OAuth2 -> URL Generator -> tick "bot", chọn permissions (Send Messages, Read Messages...). Copy URL invite, paste vào browser, mời bot vào server.
Setup Node.js project
# Cài Node.js LTS qua nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install --lts
nvm use --lts
node --version # v22.x hoặc mới hơn
# Tạo project
mkdir -p ~/bots/discord-bot && cd ~/bots/discord-bot
npm init -y
npm install discord.js dotenv
# .env
cat > .env << 'EOF'
DISCORD_TOKEN=your_token_here
EOF
chmod 600 .envCode bot mẫu
File: ~/bots/discord-bot/index.js
require('dotenv').config();
const { Client, GatewayIntentBits, Events } = require('discord.js');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
client.once(Events.ClientReady, (c) => {
console.log(`[ready] Logged in as ${c.user.tag}`);
});
client.on(Events.MessageCreate, async (message) => {
if (message.author.bot) return;
if (message.content === '!ping') {
const sent = await message.reply('Pinging...');
const latency = sent.createdTimestamp - message.createdTimestamp;
await sent.edit(`Pong! ${latency}ms (WebSocket: ${client.ws.ping}ms)`);
}
if (message.content === '!help') {
await message.reply(
'Lệnh:\n`!ping` - check latency\n`!help` - help\n`!info` - bot info'
);
}
if (message.content === '!info') {
await message.reply(
`Bot chạy trên VPS Ubuntu. Uptime: ${Math.floor(client.uptime / 1000)}s. ` +
`Phục vụ ${client.guilds.cache.size} server.`
);
}
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('[shutdown] SIGTERM received');
client.destroy();
process.exit(0);
});
client.login(process.env.DISCORD_TOKEN);Test:
cd ~/bots/discord-bot
node index.js
# Gõ !ping trong Discord channel có bot5. Chạy 24/7 với systemd (quan trọng nhất)
tmux/screen được, nhưng systemd mới là chuẩn production: auto-restart khi crash, auto-start khi reboot VPS, log centralized với journalctl.
Service cho Python bot
File: /etc/systemd/system/tele-bot.service (sudo edit)
[Unit]
Description=Telegram Bot (aiogram)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=botuser
Group=botuser
WorkingDirectory=/home/botuser/bots/tele-bot
EnvironmentFile=/home/botuser/bots/tele-bot/.env
ExecStart=/home/botuser/bots/tele-bot/.venv/bin/python /home/botuser/bots/tele-bot/bot.py
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
# Security
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/home/botuser/bots/tele-bot
[Install]
WantedBy=multi-user.targetService cho Node.js bot
File: /etc/systemd/system/discord-bot.service
[Unit]
Description=Discord Bot (discord.js v14)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=botuser
Group=botuser
WorkingDirectory=/home/botuser/bots/discord-bot
Environment=NODE_ENV=production
ExecStart=/home/botuser/.nvm/versions/node/v22.11.0/bin/node /home/botuser/bots/discord-bot/index.js
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetln -s ~/.nvm/versions/node/v22.11.0/bin/node /usr/local/bin/node-bot rồi ExecStart=/usr/local/bin/node-bot ...Enable + start service
sudo systemctl daemon-reload
# Telegram bot
sudo systemctl enable --now tele-bot
sudo systemctl status tele-bot
# Discord bot
sudo systemctl enable --now discord-bot
sudo systemctl status discord-bot
# Xem log realtime
sudo journalctl -u tele-bot -f
sudo journalctl -u discord-bot -f
# Restart sau khi update code
sudo systemctl restart tele-bot
# Stop
sudo systemctl stop tele-bot6. Auto-deploy khi git push (workflow đơn giản)
# Trên VPS: setup bare repo nhận push
mkdir -p ~/repos/tele-bot.git
cd ~/repos/tele-bot.git
git init --bare
# Tạo post-receive hook để auto-deploy
cat > hooks/post-receive << 'EOF'
#!/bin/bash
TARGET="/home/botuser/bots/tele-bot"
GIT_DIR="/home/botuser/repos/tele-bot.git"
BRANCH="main"
while read oldrev newrev ref; do
if [[ $ref = refs/heads/$BRANCH ]]; then
echo "Deploying $BRANCH to $TARGET"
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
cd $TARGET
source .venv/bin/activate
uv pip install -r requirements.txt 2>/dev/null || true
sudo systemctl restart tele-bot
echo "Deployed OK"
fi
done
EOF
chmod +x hooks/post-receive
# Cho phép botuser restart systemd service không cần password
sudo visudo
# Thêm: botuser ALL=NOPASSWD: /bin/systemctl restart tele-botTrên máy local:
cd your-bot-project
git remote add vps botuser@your-vps-ip:/home/botuser/repos/tele-bot.git
git push vps main
# VPS sẽ auto checkout + restart bot7. Monitoring uptime - biết khi bot chết
Cách 1: Healthcheck endpoint từ bot
Thêm vào bot 1 HTTP endpoint check alive. Bot Python aiogram + aiohttp:
from aiohttp import web
async def health(request):
return web.json_response({"status": "ok"})
async def start_health_server():
app = web.Application()
app.router.add_get("/health", health)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "0.0.0.0", 8080)
await site.start()
# Trong main():
asyncio.create_task(start_health_server())Mở port firewall: sudo ufw allow 8080.
Cách 2: Uptime Kuma (self-hosted, free)
# Cài Docker nếu chưa
curl -fsSL https://get.docker.com | sh
# Chạy Uptime Kuma
docker run -d --restart=always -p 3001:3001 \
-v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1
# Mở firewall + truy cập http://your-vps-ip:3001
sudo ufw allow 3001
# Setup admin, thêm monitor:
# - Type: HTTP(s)
# - URL: http://localhost:8080/health
# - Interval: 60s
# - Notification: Telegram (input bot token + chat_id)Cách 3: Healthchecks.io (free, no setup)
# Đăng ký healthchecks.io, tạo check, copy ping URL
# Trong bot, gửi ping định kỳ:
import aiohttp
PING_URL = "https://hc-ping.com/your-uuid-here"
async def ping_healthcheck():
while True:
try:
async with aiohttp.ClientSession() as s:
await s.get(PING_URL, timeout=5)
except Exception:
pass
await asyncio.sleep(60) # ping mỗi phút
# Trong main(): asyncio.create_task(ping_healthcheck())Nếu 5 phút không ping, healthchecks.io gửi email/Slack/Telegram alert.
8. Log rotation
systemd journal mặc định không xoá log cũ - sẽ ăn hết disk. Limit size:
# Edit /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=14day
sudo systemctl restart systemd-journald
# Check size hiện tại
journalctl --disk-usage9. Cập nhật dependency an toàn
# Python bot
cd ~/bots/tele-bot
source .venv/bin/activate
uv pip install --upgrade aiogram
# Test local trước khi restart service
python bot.py # Ctrl+C nếu OK
sudo systemctl restart tele-bot
# Node bot
cd ~/bots/discord-bot
npm update discord.js
node index.js # test
sudo systemctl restart discord-bot10. Cấu hình tối ưu chi phí
1 VPS 20 (199k) chạy được nhiều bot nhỏ cùng lúc nếu mỗi bot không nặng:
- 1 bot Telegram aiogram: ~30-80MB RAM idle.
- 1 bot Discord discord.js (5-10 server): ~80-150MB RAM.
- 1 Uptime Kuma container: ~150MB RAM.
- Postgres/Redis nếu cần: 100-200MB.
Tổng vẫn dư trong 2GB. Lên VPS 30/50 khi bot có DB nặng, AI inference, hoặc traffic tăng.
11. Kết luận
Stack systemd + journalctl + Uptime Kuma đã chứng minh là combo bền vững nhất cho bot 24/7 - đơn giản, không cần Docker phức tạp, log dễ debug, restart tự động. PM2 cũng OK cho Node nhưng systemd hợp với cả Python lẫn Node, ít một layer phức tạp. Setup 1 lần, dùng nhiều năm. Quan trọng: chọn VPS có IP fresh + uptime cao - không thì bot có code đẹp đến đâu cũng vô nghĩa.
Bài viết liên quan
Cần VPS chạy bot Telegram/Discord 24/7?
Gói VPS 20 tại TND (199.000đ/tháng, 2 vCPU, 2GB RAM, 40GB Ceph SSD Enterprise NVMe, IPv4 + IPv6 sạch) là entry-level lý tưởng cho bot Python/Node.js. Datacenter VN/US, khởi tạo 60s, uptime 99,9%, support tiếng Việt 24/7.
Xem bảng giá Cloud VPS- Transfer domain từ GoDaddy, Namecheap về TND
- Proxy cho SEO SERP tracking 2026: multi-country
- VPS Việt Nam 200k vs Vultr 6 USD: dev solo chọn cái nào?
- Build AI Slack bot tự host bằng Claude API trên VPS
- Thuê VPS GPU chạy DeepSeek / LLM local: chi phí thật + benchmark
- Dùng proxy hợp pháp VN 2026: Luật BVDLCN 91/2025



