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.
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/
cd /opt/mybot
sudo -u botuser python3 -m venv venv
sudo -u botuser venv/bin/pip install -r requirements.txt
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.
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").
sudo systemctl daemon-reload
sudo systemctl enable mybot # start on boot
sudo systemctl start mybot
sudo systemctl status mybot # check it's running
# Last 50 lines
journalctl -u mybot -n 50
# Live tail
journalctl -u mybot -f
# Since last boot
journalctl -u mybot -b
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
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() →