VPS chạy bot Telegram/Discord 24/7 (Python/Node.js, systemd, monitoring)

Chia sẻ bài viết

Mục lục
💡 AI-ready skill doc: bạn đang vibe code với Claude Code/Cursor/Codex/Gemini? Copy nguyên bài này paste vào AI của bạn - nó sẽ đọc như một skill document và áp dụng đúng pattern này vào project mà không cần bạn giải thích lại. Tiện, gọn, dùng được luôn.
Minh họa VPS chạy bot Telegram Discord 24/7

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 botvCPURAMGợi ý gói
Bot Telegram đơn giản (reply, command, <1000 user/ngày)22GBVPS 20 (199k)
Bot có DB, queue, vài chục API call/giây22GBVPS 20 (199k)
Bot Discord nhiều server, cache message24GBVPS 30 (299k)
Bot AI (gọi LLM, cần concurrent), bot scrape44GBVPS 30 (299k)
Multi-bot platform (chạy 10+ bot cùng VPS)48GBVPS 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-ip

3. Telegram bot với Python aiogram 3.x

Tạo bot trên Telegram

  1. Mở Telegram, search @BotFather.
  2. Gửi /newbot, đặt tên + username (phải kết thúc bằng "bot").
  3. 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 .env

Code 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

  1. Vào discord.com/developers/applications, New Application.
  2. Tab Bot -> Add Bot. Lưu token (chỉ hiện 1 lần).
  3. Tab Bot -> bật Message Content Intent (nếu bot đọc message).
  4. 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 .env

Code 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ó bot

5. 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.target

Service 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.target
💡 Mẹo: Path Node.js từ nvm khá rắc rối với systemd vì nvm dùng bash function. Cách an toàn: dùng absolute path đến node binary, hoặc ln -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-bot

6. 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-bot

Trê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 bot

7. 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-usage

9. 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-bot
⚠️ Lưu ý: Bot Telegram dùng long-polling thì OK với mọi VPS. Nếu chuyển sang webhook, bạn cần public HTTPS endpoint (Cloudflare Tunnel hoặc Nginx + Let's Encrypt). Webhook tiết kiệm tài nguyên hơn nhưng setup phức tạp hơn.

10. 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
Chat Zalo nhận báo giá Gọi 0225.999.6666
2009
15+ năm vận hành liên tục
10+
tập đoàn lớn tin dùng
100+
doanh nghiệp SMB Việt
30 ngày
đổi key lỗi miễn phí
Phần mềm bản quyền chính hãng chúng tôi cung cấp
Bản quyền chính hãng Hóa đơn VAT đầy đủ Đổi key lỗi 30 ngày Vận hành từ 2009 MST 0200994870 Hotline 0225.999.6666