diff --git a/.dockerignore b/.dockerignore index 3a90052..f9e4ccb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -52,9 +52,12 @@ docker-compose*.yml # Documentation README.md CLAUDE.md +DEPLOY.md +README.production.md -# Nginx config +# Reverse proxy configs nginx.conf.example +Caddyfile.example # Frontend build output (not needed during image build) frontend/frontend/dist diff --git a/CLAUDE.md b/CLAUDE.md index 026adf4..0f1c9a9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -153,6 +153,12 @@ docker compose -f docker-compose.production.yml exec backend uv run flask db upg - Set `SESSION_COOKIE_SECURE=true` for HTTPS - Configure OIDC URLs to match your domain +**Reverse Proxy:** +- Example configurations provided: `nginx.conf.example` and `Caddyfile.example` +- Caddy recommended for automatic HTTPS with Let's Encrypt +- Nginx for more control and advanced configurations +- See `README.production.md` for detailed setup instructions + ## Architecture ### Application Factory Pattern diff --git a/Caddyfile.example b/Caddyfile.example new file mode 100644 index 0000000..ecb2c2e --- /dev/null +++ b/Caddyfile.example @@ -0,0 +1,98 @@ +# Example Caddyfile for production deployment +# Caddy automatically handles HTTPS with Let's Encrypt! + +# Basic configuration - Caddy handles SSL automatically +trivia.torrtle.co { + # Reverse proxy to Flask app + reverse_proxy localhost:5001 + + # Increase client upload size for images (default is 10MB) + request_body { + max_size 10MB + } + + # Enable compression + encode gzip zstd + + # Logging + log { + output file /var/log/caddy/trivia.log + format json + } +} + +# Alternative: More explicit configuration with WebSocket support +# (Though Caddy handles WebSocket upgrades automatically) +trivia.torrtle.co { + # Main reverse proxy + reverse_proxy localhost:5001 { + # Forward real client IP + header_up X-Real-IP {remote_host} + header_up X-Forwarded-For {remote_host} + header_up X-Forwarded-Proto {scheme} + header_up X-Forwarded-Host {host} + + # Health check + health_uri /api/health + health_interval 30s + health_timeout 5s + } + + # Upload size + request_body { + max_size 10MB + } + + # Compression + encode gzip zstd + + # Security headers + header { + # Enable HSTS + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + + # Prevent clickjacking + X-Frame-Options "SAMEORIGIN" + + # XSS protection + X-Content-Type-Options "nosniff" + + # Referrer policy + Referrer-Policy "strict-origin-when-cross-origin" + } + + # Logging + log { + output file /var/log/caddy/trivia.log { + roll_size 100mb + roll_keep 5 + roll_keep_for 720h + } + format json + } +} + +# Optional: Expose Celery Flower monitoring on subdomain +flower.torrtle.co { + reverse_proxy localhost:5555 + + # Optional: Basic auth for protection + basicauth { + admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR4M5.laVvNFqEAa + } +} + +# Optional: Redirect www to non-www +www.trivia.torrtle.co { + redir https://trivia.torrtle.co{uri} permanent +} + +# Optional: Development/staging environment on different subdomain +staging.trivia.torrtle.co { + reverse_proxy localhost:5002 + + # Basic auth to protect staging + basicauth { + staging $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR4M5.laVvNFqEAa + } +} diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..90e90d6 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,125 @@ +# Quick Deploy Guide + +Minimal steps to deploy the trivia app to production with Caddy. + +## Prerequisites + +- Linux server with Docker and Docker Compose installed +- Domain name pointing to your server (e.g., `trivia.torrtle.co` → your server IP) +- Port 80 and 443 open in firewall + +## 5-Minute Deploy + +### 1. Clone and Configure + +```bash +# Clone repository +git clone +cd trivia-thang + +# Configure environment +cp .env.production.example .env.production +nano .env.production # Update with your values +``` + +**Required settings in `.env.production`:** +```bash +CORS_ORIGINS=https://trivia.torrtle.co +OIDC_ISSUER=https://auth.torrtle.co +OIDC_CLIENT_SECRET=your_secret_here +OIDC_REDIRECT_URI=https://trivia.torrtle.co/api/auth/callback +FRONTEND_URL=https://trivia.torrtle.co +SESSION_COOKIE_SECURE=true +``` + +### 2. Start Application + +```bash +# Build and start +docker compose -f docker-compose.production.yml up -d + +# Initialize database +docker compose -f docker-compose.production.yml exec backend uv run flask db upgrade +``` + +### 3. Setup Caddy (Automatic HTTPS) + +```bash +# Install Caddy +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list +sudo apt update && sudo apt install caddy + +# Create minimal Caddyfile +sudo tee /etc/caddy/Caddyfile << 'EOF' +trivia.torrtle.co { + reverse_proxy localhost:5001 +} +EOF + +# Start Caddy +sudo systemctl restart caddy +``` + +### 4. Done! + +Visit `https://trivia.torrtle.co` - Caddy automatically handles HTTPS! + +## Verify Deployment + +```bash +# Check if app is running +curl http://localhost:5001/api/health + +# Check Docker containers +docker compose -f docker-compose.production.yml ps + +# View logs +docker compose -f docker-compose.production.yml logs -f backend +``` + +## Common Commands + +```bash +# Restart application +docker compose -f docker-compose.production.yml restart + +# Update application +git pull +docker compose -f docker-compose.production.yml up -d --build + +# View logs +docker compose -f docker-compose.production.yml logs -f + +# Stop application +docker compose -f docker-compose.production.yml down +``` + +## Troubleshooting + +### Can't connect to HTTPS + +Check Caddy logs: +```bash +sudo journalctl -u caddy -f +``` + +Caddy needs ports 80 and 443 open for Let's Encrypt challenge. + +### WebSocket issues + +Ensure your domain's DNS is properly configured and Caddy is running. Caddy handles WebSocket upgrades automatically. + +### Database errors + +Run migrations: +```bash +docker compose -f docker-compose.production.yml exec backend uv run flask db upgrade +``` + +--- + +For detailed documentation, see: +- `README.production.md` - Full deployment guide +- `Caddyfile.example` - Advanced Caddy configuration +- `nginx.conf.example` - Alternative Nginx configuration diff --git a/README.production.md b/README.production.md index f9a0023..fbbc148 100644 --- a/README.production.md +++ b/README.production.md @@ -67,17 +67,49 @@ sudo nginx -t sudo systemctl reload nginx ``` -#### Option B: Caddy (Simpler) +#### Option B: Caddy (Recommended - Automatic HTTPS!) -Create `/etc/caddy/Caddyfile`: +Caddy is simpler than Nginx and handles HTTPS automatically with Let's Encrypt. +```bash +# Install Caddy (if not already installed) +# Ubuntu/Debian: +sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list +sudo apt update +sudo apt install caddy + +# Copy and edit the example Caddyfile +sudo cp Caddyfile.example /etc/caddy/Caddyfile + +# Update your domain in the Caddyfile +sudo nano /etc/caddy/Caddyfile + +# Validate configuration +caddy validate --config /etc/caddy/Caddyfile + +# Restart Caddy +sudo systemctl restart caddy + +# Check status +sudo systemctl status caddy +``` + +**Simple Caddyfile (minimal):** ```caddy trivia.torrtle.co { reverse_proxy localhost:5001 } ``` -Caddy automatically handles HTTPS with Let's Encrypt! +**That's it!** Caddy automatically: +- Obtains SSL certificate from Let's Encrypt +- Handles WebSocket upgrades +- Redirects HTTP to HTTPS +- Renews certificates automatically + +See `Caddyfile.example` for advanced configuration with security headers, logging, and optional features. ## Services