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>
This commit is contained in:
Cal Corum 2026-01-15 09:51:41 -06:00
parent 9076ff6446
commit 3dad6204e6

294
DEPLOYMENT.md Normal file
View File

@ -0,0 +1,294 @@
# 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
- [ ] Add redirect URI in [Discord Developer Portal](https://discord.com/developers/applications):
- `https://gameplay.sba.manticorum.com/api/auth/discord/callback/server`
### 2. Database Setup
```bash
# 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
```bash
ssh akamai
mkdir -p ~/container-data/strat-gameplay-webapp
cd ~/container-data/strat-gameplay-webapp
```
### Step 2: Clone Repository
```bash
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`):
```bash
# 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`):
```bash
# 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`:
```yaml
# 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
```bash
# 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
```bash
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
```bash
# 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
```bash
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
```bash
# 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
```bash
# 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