Operations · 7 min read

Deploying a trading bot with PM2

ecosystem.config.js, auto-restart, log rotation, and max_memory_restart for running Polymarket bots reliably in production.

Why PM2

PM2 is a Node.js process manager. For trading bots it gives you:

  • Auto-restart on crash, with exponential backoff to prevent restart storms.
  • Persistent log files with automatic rotation so you do not fill the disk.
  • A memory ceiling: if a bot leaks memory and grows past a threshold, PM2 kills and restarts it automatically.
  • pm2 status to see all bots at a glance with uptime, restarts, and memory.

Install globally: npm install -g pm2.

ecosystem.config.js

Put this at the root of your predtools directory:

// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'poly5m-v4',
      script: 'poly5m/poly5m-v4.js',
      args: '--asset BTC --live',
      cwd: '/home/ubuntu/predtools',
      autorestart: true,
      max_restarts: 50,
      min_uptime: '10s',          // must stay up 10s to count as a successful start
      max_memory_restart: '200M',
      log_file: 'data/pm2-poly5m-v4.log',
      merge_logs: true,
      time: true,                 // prepend timestamp to each log line
    },
    {
      name: 'poly15m-v6',
      script: 'poly15m/poly15m-v6.js',
      args: '--asset BTC --live',
      cwd: '/home/ubuntu/predtools',
      autorestart: true,
      max_restarts: 50,
      min_uptime: '10s',
      max_memory_restart: '200M',
      log_file: 'data/pm2-poly15m-v6.log',
      merge_logs: true,
      time: true,
    },
    {
      name: 'polycopy',
      script: 'polycopy/polycopy.js',
      args: '--live --max-balance 50',
      cwd: '/home/ubuntu/predtools/polycopy',
      autorestart: true,
      max_restarts: 50,
      min_uptime: '30s',          // polycopy needs longer to initialize
      max_memory_restart: '400M', // puppeteer uses more memory
      log_file: '../data/pm2-polycopy.log',
      merge_logs: true,
      time: true,
    },
    {
      name: 'bot-monitor',
      script: 'poly5m/bot-monitor.js',
      cwd: '/home/ubuntu/predtools',
      autorestart: true,
      max_restarts: 20,
      min_uptime: '5s',
      max_memory_restart: '100M',
      log_file: 'data/pm2-monitor.log',
      merge_logs: true,
      time: true,
    },
  ],
}

Starting and managing

# Start all bots defined in ecosystem.config.js
pm2 start ecosystem.config.js

# Check status
pm2 status

# Live log stream for one bot
pm2 logs poly5m-v4 --lines 50

# Restart a single bot
pm2 restart poly5m-v4

# Stop all
pm2 stop all

# Delete from PM2 registry (doesn't delete files)
pm2 delete all

Surviving server reboots

PM2 can save the current process list and restore it on system startup:

# Save current process list
pm2 save

# Generate and install a startup script
pm2 startup
# Then run the command it prints, e.g.:
# sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u ubuntu --hp /home/ubuntu

After this, all saved processes restart automatically on reboot.

Log rotation

PM2's built-in log rotation prevents logs from growing indefinitely:

pm2 install pm2-logrotate

# Configure
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7       # keep 7 rotated files
pm2 set pm2-logrotate:compress true  # gzip old logs
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'  # rotate daily at midnight

Watching the restart count

If a bot is restarting frequently, the PM2 status shows the restart count. A count above 5 in a short window indicates a bug, not a transient crash.

pm2 status
# Output:
# ┌─────┬──────────────┬──────────┬──────┬───────────┬──────┬──────────┐
# │ id  │ name         │ mode     │ pid  │ status    │ ↺    │ memory   │
# ├─────┼──────────────┼──────────┼──────┼───────────┼──────┼──────────┤
# │ 0   │ poly5m-v4    │ fork     │ 1234 │ online    │ 0    │ 45.2mb   │
# │ 1   │ polycopy     │ fork     │ 1235 │ online    │ 2    │ 210.5mb  │
# └─────┴──────────────┴──────────┴──────┴───────────┴──────┴──────────┘

Environment variables in PM2

Do not hardcode secrets in ecosystem.config.js. Instead, use the env key to pass environment variables, and keep sensitive values in the system environment (set via .bashrc or a secrets manager):

{
  name: 'poly5m-v4',
  script: 'poly5m/poly5m-v4.js',
  env: {
    NODE_ENV: 'production',
    // Don't put private keys here -- use secrets.json or system env
  },
}

Our bots load credentials from secrets.json at startup. PM2 just needs to know the working directory and script path.

Summary

  • Define all bots in ecosystem.config.js with autorestart: true, max_memory_restart, and min_uptime.
  • Run pm2 save and pm2 startup to survive reboots.
  • Install pm2-logrotate to prevent disk fill: 50MB max, 7-day retention, compressed.
  • Use pm2 logs <name> for live tailing and pm2 status for a health dashboard.
  • Never put private keys in ecosystem.config.js -- keep them in secrets.json.

Related bots