← cd ..
Linux systemd DevOps

Deploy a Telegram Bot on a VPS with systemd

2026-04-15 6 min read by DNI

Running python bot.py in a terminal works for testing. For production, you need the bot to survive server reboots, restart after crashes, and log properly. systemd handles all of this — and it's already on every modern Linux VPS.

# Prerequisites

# Step 1: Set Up the Project on the VPS

Create a dedicated directory and user for the bot. Never run bots as root.

# On your VPS
sudo useradd -r -s /usr/sbin/nologin botuser
sudo mkdir -p /opt/mybot
sudo chown botuser:botuser /opt/mybot

# Upload your code (from local machine)
rsync -av ./mybot/ user@yourserver:/opt/mybot/

# Step 2: Create a Virtual Environment

cd /opt/mybot
sudo -u botuser python3 -m venv venv
sudo -u botuser venv/bin/pip install -r requirements.txt

# Step 3: Create the systemd Service File

Create /etc/systemd/system/mybot.service:

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

[Service]
Type=simple
User=botuser
WorkingDirectory=/opt/mybot
ExecStart=/opt/mybot/venv/bin/python main.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal

# Security hardening
NoNewPrivileges=true
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Important: Never put your bot token directly in the service file. Use an environment file instead.

# Step 4: Store Secrets Safely

Create /opt/mybot/.env with your secrets:

BOT_TOKEN=1234567890:ABCdef...
DATABASE_URL=postgresql://user:pass@localhost/botdb
# Secure the file
sudo chown botuser:botuser /opt/mybot/.env
sudo chmod 600 /opt/mybot/.env

Then reference it in the service file by adding under [Service]:

EnvironmentFile=/opt/mybot/.env

In your Python code, load with os.getenv("BOT_TOKEN").

# Step 5: Enable and Start

sudo systemctl daemon-reload
sudo systemctl enable mybot        # start on boot
sudo systemctl start mybot
sudo systemctl status mybot        # check it's running

# Viewing Logs

# Last 50 lines
journalctl -u mybot -n 50

# Live tail
journalctl -u mybot -f

# Since last boot
journalctl -u mybot -b

# Deploying Updates

When you push new code:

# Upload new code
rsync -av ./mybot/ user@yourserver:/opt/mybot/

# Install new dependencies (if any)
sudo -u botuser /opt/mybot/venv/bin/pip install -r /opt/mybot/requirements.txt

# Restart the bot
sudo systemctl restart mybot
sudo systemctl status mybot

Zero-downtime tip

For polling bots, a systemctl restart takes under a second — Telegram queues messages during the gap and delivers them on reconnect. For webhook bots, keep the old process alive until the new one is ready.

Want your bot deployed and hosted properly from day one? I handle the full stack — code, deployment, monitoring.

get_started() →