Setting Up a Bot on VPS/Dedicated Server
A trading bot running from a laptop is a bot that will crash at the worst possible moment: close the lid, internet drops, macOS decides to update. A VPS with proper configuration solves the uptime issue, but only if done right — not nohup script.py & in terminal and hoping for the best.
Server Selection and Configuration
For most trading bots: 2 vCPU, 4 GB RAM, 40 GB SSD is sufficient. For bots with heavy analytics or own node — 4-8 vCPU, 16-32 GB RAM.
Geographic location matters: latency to exchange affects execution speed. Binance has servers in Frankfurt and Tokyo, Bybit in Netherlands, dYdX on AWS us-east-1. For HFT (high-frequency) place server in same datacenter or as close as possible. For strategies with position holding > 1 minute — not critical.
OS: Ubuntu 22.04 LTS or Debian 12. No GUI — only headless server.
Isolation and Dependency Management
Python bot
# Update system
apt update && apt upgrade -y
apt install -y python3.11 python3.11-venv python3-pip git
# Create non-privileged user
useradd -m -s /bin/bash botuser
su - botuser
# Virtual environment
python3.11 -m venv /home/botuser/bot-env
source /home/botuser/bot-env/bin/activate
pip install -r requirements.txt
Node.js bot
# Node.js via nvm (not apt — outdated versions)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
nvm install 20
nvm use 20
cd /home/botuser/mybot && npm ci --production
Never run bot as root. If bot is compromised — minimal privileges mean minimal damage.
Systemd as Process Manager
screen, tmux, nohup are for development. For production — only systemd. It restarts process on crash, manages logs, starts on server reboot.
# /etc/systemd/system/trading-bot.service
[Unit]
Description=Trading Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=botuser
Group=botuser
WorkingDirectory=/home/botuser/mybot
# Python
ExecStart=/home/botuser/bot-env/bin/python main.py
# Node.js alternative:
# ExecStart=/home/botuser/.nvm/versions/node/v20.0.0/bin/node index.js
Restart=on-failure
RestartSec=10s
StartLimitIntervalSec=60s
StartLimitBurst=3
# Resource limits
MemoryMax=2G
CPUQuota=150%
# Environment variables from file
EnvironmentFile=/home/botuser/mybot/.env
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=trading-bot
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable trading-bot
systemctl start trading-bot
systemctl status trading-bot
# View logs
journalctl -u trading-bot -f --since "1 hour ago"
Restart=on-failure + RestartSec=10s + StartLimitBurst=3 — bot restarts on crash, but doesn't loop infinitely.
Environment Variables and Secrets
API keys never in code and never in git. File .env with restricted permissions:
# /home/botuser/mybot/.env
EXCHANGE_API_KEY=your_key_here
EXCHANGE_API_SECRET=your_secret_here
TELEGRAM_BOT_TOKEN=...
LOG_LEVEL=INFO
chmod 600 /home/botuser/mybot/.env
chown botuser:botuser /home/botuser/mybot/.env
.gitignore must contain .env. Check that .env didn't accidentally get in git history: git log --all --full-history -- .env.
Monitoring and Alerts
Bot should report problems itself. Minimal set:
import telebot
import functools
bot_notifier = telebot.TeleBot(os.environ['TELEGRAM_BOT_TOKEN'])
ADMIN_CHAT_ID = os.environ['TELEGRAM_CHAT_ID']
def notify(message: str, level: str = 'INFO'):
prefix = {'INFO': 'ℹ️', 'WARNING': '⚠️', 'ERROR': '🔴'}.get(level, '')
try:
bot_notifier.send_message(ADMIN_CHAT_ID, f"{prefix} {message}")
except Exception:
pass # don't break bot due to notification issues
# In exception handler
except Exception as e:
notify(f"Bot crashed: {type(e).__name__}: {e}", level='ERROR')
raise
External watchdog: UptimeRobot or similar pings HTTP health-endpoint of bot every 5 minutes. Bot returns 200 — alive. If not — SMS/email alert.
# Simple health endpoint (if bot has HTTP server)
from aiohttp import web
async def health(request):
return web.json_response({
'status': 'ok',
'uptime': time.time() - start_time,
'last_trade': last_trade_timestamp,
})
Server Security
# Close SSH by password, only keys
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
# UFW — open only necessary ports
ufw default deny incoming
ufw allow ssh
ufw allow 443/tcp # if there's web interface
ufw enable
# Fail2ban against SSH brute force
apt install fail2ban -y
systemctl enable fail2ban --now
Exchange API keys: set IP whitelist on exchange — only your VPS IP. Even if key leaks, won't work from other IPs.
Deploying Updates
# Deploy script
#!/bin/bash
set -e
cd /home/botuser/mybot
git pull origin main
source /home/botuser/bot-env/bin/activate
pip install -r requirements.txt --quiet
systemctl restart trading-bot
sleep 3
systemctl status trading-bot --no-pager
For more complex projects — GitHub Actions + SSH deploy automatically on push to main.
Setup takes one working day: server configuration, systemd unit, monitoring, SSH security, test bot run and verify auto-restart.







