Skip to main content

PM2 Deployment Guide

This guide covers PM2-based deployment for Hyperscape, including secrets management, database mode detection, and streaming configuration.

Overview

Hyperscape uses PM2 for production process management on Vast.ai and other GPU servers. The deployment system includes:
  • Automatic secrets loading from /tmp/hyperscape-secrets.env
  • Database mode auto-detection (local vs remote)
  • Xvfb virtual display for WebGPU streaming
  • Chrome Beta for stable streaming capture
  • Multi-platform RTMP streaming with auto-detection

Quick Start

Deploy to Vast.ai

This script:
  1. Pulls latest code from origin/main
  2. Installs system dependencies (Chrome Beta, FFmpeg, Xvfb)
  3. Loads secrets from /tmp/hyperscape-secrets.env
  4. Auto-detects database mode from DATABASE_URL
  5. Builds core packages
  6. Applies database migrations
  7. Starts Xvfb virtual display
  8. Launches duel stack via PM2

PM2 Commands

Secrets Management

Secrets File Format

PM2 loads secrets from /tmp/hyperscape-secrets.env at config load time:

Why Direct File Loading?

Problem: bunx pm2 doesn’t reliably inherit exported environment variables from the deploy shell script. Solution: ecosystem.config.cjs reads the secrets file directly at config load time:

Creating Secrets File

GitHub Actions (.github/workflows/deploy-vast.yml):
Manual Deployment:

Database Mode Auto-Detection

How It Works

The deployment system automatically detects whether to use local or remote PostgreSQL:

Local Mode

Triggers:
  • DATABASE_URL contains localhost, 127.0.0.1, 0.0.0.0, or ::1
  • OR DUEL_DATABASE_MODE=local explicitly set
Behavior:
  • Starts local PostgreSQL via pg_ctlcluster
  • Creates database and user if needed
  • Sets USE_LOCAL_POSTGRES=true

Remote Mode

Triggers:
  • DATABASE_URL contains any other hostname
  • OR DUEL_DATABASE_MODE=remote explicitly set
Behavior:
  • Uses external PostgreSQL (Neon, Railway, etc.)
  • Skips local PostgreSQL setup
  • Sets USE_LOCAL_POSTGRES=false

Manual Override

PostgreSQL Connection Pool

Configuration

Why 20 Connections?

  • Concurrent Agents: Up to 10 AI agents querying database simultaneously
  • Bank Queries: Each agent can make 5 concurrent bank queries
  • Server Queries: Game server needs connections for player data
  • Headroom: Extra capacity for spikes and migrations

Tuning

If you see “timeout exceeded when trying to connect” errors:

Xvfb Virtual Display

Why Xvfb?

WebGPU requires a window context, even on headless servers. Xvfb provides a virtual X11 display.

Startup Order

Critical: Xvfb must start BEFORE PM2:

PM2 Environment

ecosystem.config.cjs explicitly forwards DISPLAY:

Troubleshooting

Error: cannot open display Solution:

Chrome Beta Streaming

Why Chrome Beta?

  • Stability: More stable than Dev/Canary channels
  • WebGPU Support: Full WebGPU support with ANGLE backend
  • Compatibility: Better driver compatibility than native Vulkan

Installation

Configuration

ANGLE Backend Selection

Default (Recommended):
  • Automatically selects best backend for the system
  • Tries Vulkan → OpenGL → D3D11 in order
  • Most compatible across different GPUs
Vulkan (Legacy):
  • Native Vulkan backend
  • Can crash on incompatible drivers
  • Not recommended for production

RTMP Streaming

Auto-Detection

Stream destinations are auto-detected from available keys:

Supported Platforms

Manual Configuration

Health Checks

Local Services

The deploy script waits for services to become healthy:

PM2 Status

Port Proxying

Vast.ai requires port proxying for external access:

Logs

PM2 Logs

Log Files

Troubleshooting

Server Crashes on Startup

Check logs:
Common causes:
  • Missing DATABASE_URL → Check /tmp/hyperscape-secrets.env
  • Database connection timeout → Increase POSTGRES_POOL_MAX
  • WebGPU initialization failed → Check GPU driver and Xvfb

Streaming Not Starting

Check streaming state:
Common causes:
  • Missing stream keys → Check TWITCH_STREAM_KEY, KICK_STREAM_KEY
  • Xvfb not running → ps aux | grep Xvfb
  • Chrome Beta not installed → google-chrome-beta --version
  • DISPLAY not set → echo $DISPLAY (should be :99)

Database Connection Errors

Error: timeout exceeded when trying to connect Solutions:
  1. Increase connection pool:
  2. Check database is accessible:
  3. Verify connection string:

PM2 Not Forwarding Environment Variables

Problem: Environment variables set in shell are not available to PM2 processes. Solution: Use /tmp/hyperscape-secrets.env instead of shell exports:

Advanced Configuration

Custom Database Mode

Custom Streaming Configuration

Custom Xvfb Display

Monitoring

Process Health

Service Health

Auto-Restart Configuration

Deployment Checklist

  • Secrets file created at /tmp/hyperscape-secrets.env
  • DATABASE_URL set (local or remote)
  • Stream keys set (if streaming enabled)
  • Chrome Beta installed
  • FFmpeg installed
  • Xvfb running on :99
  • GPU display driver active (gpu_display_active=true on Vast.ai)
  • Port proxies configured (35143, 35079, 35144)
  • PM2 process started
  • Health checks passing
  • ecosystem.config.cjs - PM2 configuration
  • scripts/deploy-vast.sh - Deployment script
  • .github/workflows/deploy-vast.yml - CI/CD workflow
  • packages/server/.env.example - Environment variable documentation
  • docs/duel-stack.md - Duel stack documentation