strat-gameplay-webapp/DEPLOYMENT.md
Cal Corum 3dad6204e6 CLAUDE: Add production deployment documentation for Linode VPS
Documents deployment strategy using Docker Compose on existing Linode
infrastructure. Includes architecture diagram, step-by-step deployment
guide, NPM configuration, maintenance commands, and rollback procedures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 09:51:41 -06:00

8.6 KiB

Production Deployment Guide

Target Environment

Server: Linode VPS (ssh akamai)

  • Ubuntu 24.04 LTS
  • 8GB RAM / 160GB disk
  • Docker + Nginx Proxy Manager pre-configured

Domain: gameplay.sba.manticorum.com (or similar)

Architecture

┌─────────────────────────────────────────────────────┐
│                    Linode VPS                       │
├─────────────────────────────────────────────────────┤
│  Nginx Proxy Manager (:80/:443)                     │
│    └─ gameplay.sba.manticorum.com                   │
│        ├─ /api/* → backend:8000                     │
│        ├─ /socket.io/* → backend:8000 (WebSocket)   │
│        └─ /* → frontend:3000                        │
├─────────────────────────────────────────────────────┤
│  strat-gameplay-webapp                              │
│    ├─ backend (FastAPI + Socket.io) :8000           │
│    └─ frontend-sba (Nuxt 3 SSR) :3000               │
├─────────────────────────────────────────────────────┤
│  Shared Services (existing)                         │
│    ├─ sba_postgres :5432 (database: sba_gameplay)   │
│    └─ sba_redis :6379                               │
└─────────────────────────────────────────────────────┘

Why Docker Compose?

Evaluated options for this deployment:

Option Verdict Reason
Docker Compose Selected Existing infra, compose files ready, NPM network integration
Bare metal (systemd) No isolation, manual dependency management, harder rollbacks
Docker Swarm Overkill for single node
Kubernetes/K3s Significant complexity for minimal benefit at this scale

Pre-Deployment Checklist

1. Discord OAuth Setup

2. Database Setup

# SSH to server
ssh akamai

# Create database
docker exec -it sba_postgres psql -U sba -c "CREATE DATABASE sba_gameplay;"

3. DNS Configuration

  • Add A record: gameplay.sba.manticorum.com → Linode IP

Deployment Steps

Step 1: Prepare Server Directory

ssh akamai
mkdir -p ~/container-data/strat-gameplay-webapp
cd ~/container-data/strat-gameplay-webapp

Step 2: Clone Repository

git clone https://github.com/calcorum/strat-gameplay-webapp.git .
# Or use deploy key for private repo

Step 3: Create Production Environment Files

Backend (backend/.env):

# Database - use existing SBA postgres
DATABASE_URL=postgresql+asyncpg://sba:PASSWORD@sba_postgres:5432/sba_gameplay

# Security
SECRET_KEY=generate-a-secure-64-char-key-here

# Discord OAuth
DISCORD_CLIENT_ID=your-client-id
DISCORD_CLIENT_SECRET=your-client-secret
DISCORD_SERVER_REDIRECT_URI=https://gameplay.sba.manticorum.com/api/auth/discord/callback/server
FRONTEND_URL=https://gameplay.sba.manticorum.com

# CORS
CORS_ORIGINS=["https://gameplay.sba.manticorum.com"]

# Redis - use existing SBA redis
REDIS_URL=redis://sba_redis:6379

# League APIs
SBA_API_URL=https://api.sba.manticorum.com
SBA_API_KEY=your-sba-api-key

# Access Control (empty = all users, or comma-separated Discord IDs)
ALLOWED_DISCORD_IDS=

Frontend (frontend-sba/.env):

# API URLs (same domain, proxied by NPM)
NUXT_PUBLIC_API_URL=https://gameplay.sba.manticorum.com
NUXT_PUBLIC_WS_URL=https://gameplay.sba.manticorum.com

# Discord OAuth
NUXT_PUBLIC_DISCORD_CLIENT_ID=your-client-id
NUXT_PUBLIC_DISCORD_REDIRECT_URI=https://gameplay.sba.manticorum.com/auth/callback

# Internal API URL for SSR (Docker network)
NUXT_API_URL_INTERNAL=http://backend:8000

Step 4: Create Production Docker Compose Override

Create docker-compose.production.yml:

# Production overrides for Linode deployment
# Usage: docker compose -f docker-compose.yml -f docker-compose.production.yml up -d

services:
  backend:
    build:
      context: ./backend
      target: production
    environment:
      - APP_ENV=production
      - DEBUG=false
    networks:
      - sba-database_default
      - nginx-proxy-manager_npm_network
    restart: always
    # Don't expose port externally - NPM handles routing
    ports: !reset []
    expose:
      - "8000"

  frontend-sba:
    build:
      context: ./frontend-sba
      target: production
    environment:
      - NODE_ENV=production
    networks:
      - nginx-proxy-manager_npm_network
    restart: always
    ports: !reset []
    expose:
      - "3000"
    depends_on:
      - backend

  # Don't need local redis - use existing sba_redis
  redis:
    profiles:
      - disabled

networks:
  sba-database_default:
    external: true
  nginx-proxy-manager_npm_network:
    external: true

Step 5: Run Database Migrations

# Build backend first
docker compose -f docker-compose.yml -f docker-compose.production.yml build backend

# Run migrations
docker compose -f docker-compose.yml -f docker-compose.production.yml run --rm backend \
  alembic upgrade head

Step 6: Start Services

docker compose -f docker-compose.yml -f docker-compose.production.yml up -d

Step 7: Configure Nginx Proxy Manager

Access NPM at http://LINODE_IP:81 and create proxy host:

Proxy Host Settings:

  • Domain: gameplay.sba.manticorum.com
  • Scheme: http
  • Forward Hostname: strat-gameplay-webapp-frontend-sba-1
  • Forward Port: 3000
  • Websockets Support: Enabled
  • SSL: Request new Let's Encrypt certificate

Custom Locations (for API routing):

Location Scheme Forward Host Port
/api http strat-gameplay-webapp-backend-1 8000
/socket.io http strat-gameplay-webapp-backend-1 8000

Step 8: Verify Deployment

# Health check
curl https://gameplay.sba.manticorum.com/api/health

# Check logs
docker compose -f docker-compose.yml -f docker-compose.production.yml logs -f

Maintenance Commands

cd ~/container-data/strat-gameplay-webapp

# View logs
docker compose -f docker-compose.yml -f docker-compose.production.yml logs -f backend
docker compose -f docker-compose.yml -f docker-compose.production.yml logs -f frontend-sba

# Restart services
docker compose -f docker-compose.yml -f docker-compose.production.yml restart

# Pull updates and redeploy
git pull
docker compose -f docker-compose.yml -f docker-compose.production.yml build
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d

# Run migrations after update
docker compose -f docker-compose.yml -f docker-compose.production.yml run --rm backend \
  alembic upgrade head

Rollback Procedure

# Check previous commits
git log --oneline -10

# Rollback to specific commit
git checkout <commit-hash>

# Rebuild and restart
docker compose -f docker-compose.yml -f docker-compose.production.yml build
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d

Monitoring

Health Endpoints

  • Backend: https://gameplay.sba.manticorum.com/api/health
  • Frontend: https://gameplay.sba.manticorum.com (200 OK)

Log Locations

# Container logs
docker logs strat-gameplay-webapp-backend-1

# Application logs (if volume mounted)
~/container-data/strat-gameplay-webapp/backend/logs/

Resource Estimates

Service RAM CPU Notes
Backend ~200MB Low Increases with concurrent games
Frontend ~150MB Low Nuxt SSR
Total ~350MB Low Well within 5.8GB available

Security Considerations

  • Use strong SECRET_KEY (64+ characters)
  • Restrict ALLOWED_DISCORD_IDS for beta testing
  • SSL enforced via NPM
  • Database credentials not exposed externally
  • Redis not exposed externally (Docker network only)

Future Scaling Options

If load increases beyond single-server capacity:

  1. Vertical: Upgrade Linode plan (easy, immediate)
  2. Database: Migrate to managed PostgreSQL (Linode, Neon, Supabase)
  3. Horizontal: Add second app server behind load balancer
  4. CDN: Cloudflare for static assets and DDoS protection