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:
parent
9076ff6446
commit
3dad6204e6
294
DEPLOYMENT.md
Normal file
294
DEPLOYMENT.md
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user