Installation¶
Step-by-step deployment of a QS-Bridge cluster on a VPS.
Overview¶
A complete QS-Bridge deployment consists of five services:
graph TD
VPS
VPS --> STDB["SpacetimeDB (port 3000) — database engine"]
VPS --> Platform["Platform STDB Module — WASM module published to SpacetimeDB"]
VPS --> Gateway["API Gateway (port 3081) — Hono server, auth + admin API"]
VPS --> Panel["Panel (static) — React SPA served by gateway or nginx"]
VPS --> Nginx["Nginx (port 443) — TLS termination, reverse proxy"]
Step 1 — Clone the Repository¶
Step 2 — Start SpacetimeDB¶
# Start the SpacetimeDB server
spacetime start
# Verify it's running
spacetime version
# Should show: spacetimedb-cli 1.1.x
SpacetimeDB runs on port 3000 by default. For production, consider running it as a systemd service:
# /etc/systemd/system/spacetimedb.service
[Unit]
Description=SpacetimeDB Server
After=network.target
[Service]
Type=simple
User=spacetimedb
ExecStart=/usr/local/bin/spacetime start
Restart=always
RestartSec=5
Environment=SPACETIMEDB_LOG=info
[Install]
WantedBy=multi-user.target
Step 3 — Build and Publish the Platform Module¶
cd stdb-module
# Build the WASM module
spacetime build
# Publish to local SpacetimeDB
spacetime publish qs-bridge
# Verify
spacetime sql qs-bridge "SELECT * FROM module_config"
The init reducer runs automatically on first publish, creating the ModuleConfig row with owner_identity set to the publishing identity.
Step 4 — Bootstrap the First Admin¶
# Option A: Use the bootstrap script
./panel/bootstrap-admin.sh
# Option B: Manual SQL insert
spacetime sql qs-bridge \
"INSERT INTO admin_role (player_id, level, granted_by, granted_at) \
VALUES ('Steam:YOUR_STEAM64_ID', 0, 'Steam:YOUR_STEAM64_ID', 0)"
Admin levels: 0 = SuperAdmin (Owner), 1 = Admin, 2 = Moderator.
Step 5 — Configure Environment¶
Edit .env:
# Required
GATEWAY_PORT=3081
GATEWAY_URL=https://panel.qs-zuq.com
JWT_SECRET=$(openssl rand -hex 32)
STDB_HOST=ws://localhost:3000
STDB_TOKEN=$(spacetime identity token)
# Optional: BisectHosting integration
BISECT_API_KEY=your-api-key-here
Step 6 — Build and Start the API Gateway¶
For production, create a systemd service:
# /etc/systemd/system/qs-gateway.service
[Unit]
Description=QS-Bridge API Gateway
After=network.target spacetimedb.service
[Service]
Type=simple
User=qs-bridge
WorkingDirectory=/opt/qs-bridge/panel/api
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
EnvironmentFile=/opt/qs-bridge/panel/api/.env
[Install]
WantedBy=multi-user.target
Step 7 — Build the Panel¶
cd panel/ui
# Install dependencies
npm ci
# Build for production
VITE_API_URL=/api \
VITE_STDB_HOST=wss://panel.qs-zuq.com/stdb \
npm run build
# Output: dist/
Step 8 — Configure Nginx¶
# /etc/nginx/sites-available/qs-panel
server {
listen 443 ssl http2;
server_name panel.qs-zuq.com;
ssl_certificate /etc/letsencrypt/live/panel.qs-zuq.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/panel.qs-zuq.com/privkey.pem;
# Panel static files
root /opt/qs-bridge/panel/ui/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# API gateway proxy
location /api/ {
proxy_pass http://127.0.0.1:3081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Auth routes proxy
location /auth/ {
proxy_pass http://127.0.0.1:3081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# SpacetimeDB WebSocket proxy
location /stdb/ {
proxy_pass http://127.0.0.1:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
# Cache static assets (content-hashed)
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# HTTP → HTTPS redirect
server {
listen 80;
server_name panel.qs-zuq.com;
return 301 https://$host$request_uri;
}
sudo ln -s /etc/nginx/sites-available/qs-panel /etc/nginx/sites-enabled/
sudo certbot --nginx -d panel.qs-zuq.com
sudo systemctl reload nginx
Step 9 — Deploy the Bridge (per game server)¶
On each game server:
# Copy the bridge library
scp libqsbridge.so gameserver:/opt/qs-bridge/
# Configure environment
cat > /opt/qs-bridge/bridge.env << 'EOF'
QSB_SERVER_ID=server-01
QSB_STDB_HOST=ws://your-vps:3000
QSB_STDB_MODULE=qs-bridge
QSB_HMAC_SECRET=your-shared-secret
EOF
# Start game server with bridge
source /opt/qs-bridge/bridge.env
LD_PRELOAD=/opt/qs-bridge/libqsbridge.so ./GameServer-Linux-Shipping
Verify Deployment¶
# 1. SpacetimeDB is running
spacetime sql qs-bridge "SELECT * FROM module_config"
# 2. Gateway is healthy
curl -s http://localhost:3081/api/health | jq .
# 3. Panel is accessible
curl -sI https://panel.qs-zuq.com
# 4. Bridge registered (after starting a game server)
spacetime sql qs-bridge "SELECT server_id, status, online_count FROM server_registry"
Service Summary¶
| Service | Port | Managed By | Auto-Restart |
|---|---|---|---|
| SpacetimeDB | 3000 | systemd | ✅ |
| API Gateway | 3081 | systemd | ✅ |
| Nginx | 443/80 | systemd | ✅ |
| Bridge (.so) | — | Game server | Via game server |
Related Pages¶
- Prerequisites — what you need before starting
- BisectHosting — hosting provider integration
- Configuration — all environment variables
- Troubleshooting — common issues