From e9de71ce29a4166b21fc60229df5c6a5ca84e7c6 Mon Sep 17 00:00:00 2001 From: kyle Date: Sat, 31 Jan 2026 20:09:48 +0100 Subject: [PATCH] feat: add Digital Ocean deployment configuration - Create docker-compose.digitalocean.yml for registry-based deployment - Add .env.digitalocean.example template for cloud deployment - Add comprehensive DIGITAL_OCEAN_DEPLOYMENT.md guide - Configure image pulling from Gitea registry - Include SSL setup with Caddy/Traefik - Add backup, monitoring, and security instructions Co-Authored-By: Claude Sonnet 4.5 --- .env.digitalocean.example | 46 ++++ DIGITAL_OCEAN_DEPLOYMENT.md | 459 ++++++++++++++++++++++++++++++++ docker-compose.digitalocean.yml | 137 ++++++++++ 3 files changed, 642 insertions(+) create mode 100644 .env.digitalocean.example create mode 100644 DIGITAL_OCEAN_DEPLOYMENT.md create mode 100644 docker-compose.digitalocean.yml diff --git a/.env.digitalocean.example b/.env.digitalocean.example new file mode 100644 index 0000000..37baa5a --- /dev/null +++ b/.env.digitalocean.example @@ -0,0 +1,46 @@ +# ========================================== +# VIP Coordinator - Digital Ocean Environment +# ========================================== +# Copy this file to .env.digitalocean and fill in your values +# Then deploy with: docker-compose -f docker-compose.digitalocean.yml --env-file .env.digitalocean up -d + +# ========================================== +# Gitea Registry Configuration +# ========================================== +# Your local Gitea server (accessible from Digital Ocean) +# If Gitea is on your LAN, you'll need to expose it or use a VPN +GITEA_REGISTRY=YOUR_PUBLIC_GITEA_URL:3000 +IMAGE_TAG=latest + +# ========================================== +# Database Configuration +# ========================================== +POSTGRES_DB=vip_coordinator +POSTGRES_USER=vip_user +POSTGRES_PASSWORD=CHANGE_ME_TO_STRONG_PASSWORD_12345 + +# ========================================== +# Auth0 Configuration +# ========================================== +# Get these from your Auth0 dashboard +# IMPORTANT: Update Auth0 callbacks to use your production domain +AUTH0_DOMAIN=dev-s855cy3bvjjbkljt.us.auth0.com +AUTH0_AUDIENCE=https://vip-coordinator-api +AUTH0_ISSUER=https://dev-s855cy3bvjjbkljt.us.auth0.com/ +AUTH0_CLIENT_ID=JXEVOIfS5eYCkeKbbCWIkBYIvjqdSP5d + +# ========================================== +# Frontend Configuration +# ========================================== +# Port 80 for HTTP (will be behind reverse proxy for HTTPS) +FRONTEND_PORT=80 + +# ========================================== +# Optional: External APIs +# ========================================== +AVIATIONSTACK_API_KEY= + +# ========================================== +# Optional: Database Seeding +# ========================================== +RUN_SEED=false diff --git a/DIGITAL_OCEAN_DEPLOYMENT.md b/DIGITAL_OCEAN_DEPLOYMENT.md new file mode 100644 index 0000000..a2ff19e --- /dev/null +++ b/DIGITAL_OCEAN_DEPLOYMENT.md @@ -0,0 +1,459 @@ +# VIP Coordinator - Digital Ocean Deployment Guide + +## Overview + +This guide walks you through deploying VIP Coordinator to Digital Ocean using pre-built Docker images from your Gitea registry. + +## Prerequisites + +- [ ] Digital Ocean account +- [ ] Docker images pushed to Gitea registry (completed ✅) +- [ ] Domain name (recommended) or will use droplet IP +- [ ] Auth0 account configured + +## Architecture + +``` +Internet + │ + ├─> Your Domain (optional) + │ + ↓ +[Digital Ocean Droplet] + │ + ├─> Caddy/Traefik (Reverse Proxy + SSL) + │ ↓ + ├─> Frontend Container (port 80) + │ ↓ + ├─> Backend Container (port 3000) + │ ↓ + ├─> PostgreSQL Container + │ ↓ + └─> Redis Container +``` + +## Step 1: Create Digital Ocean Droplet + +### Recommended Specifications + +**Minimum (Testing):** +- **Size:** Basic Droplet - $12/month +- **RAM:** 2GB +- **CPU:** 1 vCPU +- **Storage:** 50GB SSD +- **Region:** Choose closest to your users + +**Recommended (Production):** +- **Size:** General Purpose - $24/month +- **RAM:** 4GB +- **CPU:** 2 vCPUs +- **Storage:** 80GB SSD +- **Region:** Choose closest to your users + +### Create Droplet + +1. Go to [Digital Ocean](https://cloud.digitalocean.com/droplets/new) +2. **Choose Image:** Ubuntu 24.04 LTS x64 +3. **Choose Size:** Select based on recommendations above +4. **Choose Region:** Select closest region +5. **Authentication:** SSH keys (recommended) or password +6. **Hostname:** `vip-coordinator` +7. **Tags:** `production`, `vip-coordinator` +8. **Backups:** Enable weekly backups (recommended) +9. Click **Create Droplet** + +## Step 2: Initial Server Setup + +### SSH into Droplet + +```bash +ssh root@YOUR_DROPLET_IP +``` + +### Update System + +```bash +apt update && apt upgrade -y +``` + +### Create Non-Root User + +```bash +adduser vipcoord +usermod -aG sudo vipcoord +usermod -aG docker vipcoord # Will add docker group later +``` + +### Configure Firewall (UFW) + +```bash +# Enable UFW +ufw default deny incoming +ufw default allow outgoing + +# Allow SSH +ufw allow OpenSSH + +# Allow HTTP and HTTPS +ufw allow 80/tcp +ufw allow 443/tcp + +# Enable firewall +ufw enable + +# Check status +ufw status +``` + +## Step 3: Install Docker + +```bash +# Install Docker +curl -fsSL https://get.docker.com -o get-docker.sh +sh get-docker.sh + +# Add user to docker group +usermod -aG docker vipcoord + +# Install Docker Compose +apt install docker-compose-plugin -y + +# Verify installation +docker --version +docker compose version +``` + +## Step 4: Configure Gitea Registry Access + +### Option A: Public Gitea (Recommended) + +If your Gitea is publicly accessible: + +```bash +# Login to Gitea registry +docker login YOUR_PUBLIC_GITEA_URL:3000 -u kyle +# Enter your Gitea token: 2f4370ce710a4a1f84e8bf6c459fe63041376c0e +``` + +### Option B: Gitea on LAN (Requires VPN/Tunnel) + +If your Gitea is on LAN (192.168.68.53): + +**Solutions:** +1. **Tailscale VPN** (Recommended) + - Install Tailscale on both your local machine and Digital Ocean droplet + - Access Gitea via Tailscale IP + +2. **SSH Tunnel** + ```bash + # On your local machine + ssh -L 3000:192.168.68.53:3000 root@YOUR_DROPLET_IP + ``` + +3. **Expose Gitea Publicly** (Not Recommended for Security) + - Configure port forwarding on your router + - Use dynamic DNS service + - Set up Cloudflare tunnel + +### Option C: Alternative - Push to Docker Hub + +If Gitea access is complex, push images to Docker Hub instead: + +```bash +# On your local machine +docker tag 192.168.68.53:3000/kyle/vip-coordinator/backend:latest kyle/vip-coordinator-backend:latest +docker tag 192.168.68.53:3000/kyle/vip-coordinator/frontend:latest kyle/vip-coordinator-frontend:latest + +docker push kyle/vip-coordinator-backend:latest +docker push kyle/vip-coordinator-frontend:latest +``` + +Then update `docker-compose.digitalocean.yml` to use Docker Hub images. + +## Step 5: Deploy Application + +### Copy Files to Droplet + +```bash +# On your local machine +scp docker-compose.digitalocean.yml root@YOUR_DROPLET_IP:/home/vipcoord/ +scp .env.digitalocean.example root@YOUR_DROPLET_IP:/home/vipcoord/ +``` + +### Configure Environment + +```bash +# On droplet +cd /home/vipcoord + +# Copy and edit environment file +cp .env.digitalocean.example .env.digitalocean +nano .env.digitalocean +``` + +**Update these values:** +```env +# If using LAN Gitea via Tailscale +GITEA_REGISTRY=100.x.x.x:3000 + +# If using public Gitea +GITEA_REGISTRY=gitea.yourdomain.com:3000 + +# If using Docker Hub +# Comment out GITEA_REGISTRY and update image names in docker-compose + +# Strong database password +POSTGRES_PASSWORD=YOUR_STRONG_PASSWORD_HERE + +# Auth0 configuration (same as before) +AUTH0_DOMAIN=dev-s855cy3bvjjbkljt.us.auth0.com +AUTH0_CLIENT_ID=JXEVOIfS5eYCkeKbbCWIkBYIvjqdSP5d +AUTH0_AUDIENCE=https://vip-coordinator-api +AUTH0_ISSUER=https://dev-s855cy3bvjjbkljt.us.auth0.com/ +``` + +### Start Services + +```bash +# Start all services +docker compose -f docker-compose.digitalocean.yml --env-file .env.digitalocean up -d + +# Check status +docker compose -f docker-compose.digitalocean.yml ps + +# View logs +docker compose -f docker-compose.digitalocean.yml logs -f +``` + +## Step 6: Set Up Reverse Proxy with SSL + +### Option A: Caddy (Recommended - Easiest) + +Create `Caddyfile`: + +```bash +nano Caddyfile +``` + +```caddy +your-domain.com { + reverse_proxy localhost:80 +} +``` + +Run Caddy: + +```bash +docker run -d \ + --name caddy \ + -p 80:80 \ + -p 443:443 \ + -v /home/vipcoord/Caddyfile:/etc/caddy/Caddyfile \ + -v caddy_data:/data \ + -v caddy_config:/config \ + --restart unless-stopped \ + caddy:latest +``` + +Caddy automatically handles: +- SSL certificate from Let's Encrypt +- HTTP to HTTPS redirect +- Certificate renewal + +### Option B: Traefik + +Create `docker-compose.traefik.yml`: + +```yaml +version: '3.8' + +services: + traefik: + image: traefik:v2.10 + command: + - "--api.insecure=true" + - "--providers.docker=true" + - "--entrypoints.web.address=:80" + - "--entrypoints.websecure.address=:443" + - "--certificatesresolvers.letsencrypt.acme.email=your@email.com" + - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" + - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web" + ports: + - "80:80" + - "443:443" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock:ro" + - "./letsencrypt:/letsencrypt" + restart: unless-stopped +``` + +## Step 7: Configure Auth0 + +Update Auth0 application settings: + +1. Go to [Auth0 Dashboard](https://manage.auth0.com/) +2. Select your application +3. **Allowed Callback URLs:** Add `https://your-domain.com` +4. **Allowed Web Origins:** Add `https://your-domain.com` +5. **Allowed Logout URLs:** Add `https://your-domain.com` +6. Click **Save Changes** + +## Step 8: Database Backups + +### Automated Daily Backups + +Create backup script: + +```bash +nano /home/vipcoord/backup-db.sh +``` + +```bash +#!/bin/bash +BACKUP_DIR="/home/vipcoord/backups" +TIMESTAMP=$(date +%Y%m%d_%H%M%S) + +mkdir -p $BACKUP_DIR + +docker exec vip-coordinator-postgres pg_dump -U vip_user vip_coordinator | gzip > $BACKUP_DIR/vip_coordinator_$TIMESTAMP.sql.gz + +# Keep only last 7 days +find $BACKUP_DIR -name "vip_coordinator_*.sql.gz" -mtime +7 -delete +``` + +Make executable and add to cron: + +```bash +chmod +x /home/vipcoord/backup-db.sh + +# Add to crontab (daily at 2 AM) +crontab -e +# Add this line: +0 2 * * * /home/vipcoord/backup-db.sh +``` + +## Step 9: Monitoring and Logging + +### View Logs + +```bash +# All services +docker compose -f docker-compose.digitalocean.yml logs -f + +# Specific service +docker compose -f docker-compose.digitalocean.yml logs -f backend + +# Last 100 lines +docker compose -f docker-compose.digitalocean.yml logs --tail=100 backend +``` + +### Check Container Health + +```bash +docker ps +docker compose -f docker-compose.digitalocean.yml ps +``` + +### Monitor Resources + +```bash +# Real-time resource usage +docker stats + +# Disk usage +df -h +docker system df +``` + +## Step 10: Updating Application + +When you push new images to Gitea: + +```bash +# On droplet +cd /home/vipcoord + +# Pull latest images +docker compose -f docker-compose.digitalocean.yml pull + +# Restart with new images +docker compose -f docker-compose.digitalocean.yml down +docker compose -f docker-compose.digitalocean.yml up -d + +# Verify +docker compose -f docker-compose.digitalocean.yml ps +docker compose -f docker-compose.digitalocean.yml logs -f +``` + +## Troubleshooting + +### Application Not Accessible + +1. Check firewall: `ufw status` +2. Check containers: `docker ps` +3. Check logs: `docker compose logs -f` +4. Check Auth0 callback URLs match your domain + +### Database Connection Issues + +```bash +# Check postgres is running +docker exec vip-coordinator-postgres pg_isready -U vip_user + +# Check backend can connect +docker compose logs backend | grep -i database +``` + +### SSL Certificate Issues + +```bash +# Caddy logs +docker logs caddy + +# Force certificate renewal +docker exec caddy caddy reload --config /etc/caddy/Caddyfile +``` + +## Security Checklist + +- [ ] Firewall configured (only 22, 80, 443 open) +- [ ] SSH key authentication (disable password auth) +- [ ] Non-root user for application +- [ ] Strong database password +- [ ] Auth0 callbacks restricted to production domain +- [ ] Automated backups configured +- [ ] SSL/TLS enabled +- [ ] Regular system updates scheduled +- [ ] Fail2ban installed for SSH protection +- [ ] Docker containers run as non-root users + +## Cost Estimation + +**Monthly Costs:** +- Droplet (4GB): $24/month +- Backups (20%): ~$5/month +- **Total:** ~$29/month + +**Optional:** +- Domain name: $10-15/year +- Digital Ocean Managed Database (if scaling): $15/month+ + +## Support + +- **Digital Ocean Docs:** https://docs.digitalocean.com/ +- **Docker Docs:** https://docs.docker.com/ +- **Auth0 Docs:** https://auth0.com/docs + +## Next Steps + +1. Set up domain name (optional but recommended) +2. Configure monitoring (Uptime Robot, etc.) +3. Set up log aggregation (Digital Ocean Monitoring, Papertrail) +4. Configure automated updates +5. Add staging environment for testing + +--- + +**Deployment completed!** 🚀 + +Your VIP Coordinator is now live at `https://your-domain.com` diff --git a/docker-compose.digitalocean.yml b/docker-compose.digitalocean.yml new file mode 100644 index 0000000..ce77f19 --- /dev/null +++ b/docker-compose.digitalocean.yml @@ -0,0 +1,137 @@ +version: '3.8' + +# ========================================== +# VIP Coordinator - Digital Ocean Deployment +# ========================================== +# This compose file pulls pre-built images from Gitea registry +# No local builds required - perfect for cloud deployment + +services: + # PostgreSQL Database + postgres: + image: postgres:16-alpine + container_name: vip-coordinator-postgres + environment: + POSTGRES_DB: ${POSTGRES_DB:-vip_coordinator} + POSTGRES_USER: ${POSTGRES_USER:-vip_user} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} + volumes: + - vip-coordinator-postgres-data:/var/lib/postgresql/data + networks: + - vip-coordinator-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-vip_user}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # Redis Cache + redis: + image: redis:7-alpine + container_name: vip-coordinator-redis + volumes: + - vip-coordinator-redis-data:/data + networks: + - vip-coordinator-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + start_period: 5s + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + command: redis-server --appendonly yes + + # NestJS Backend API (from Gitea registry) + backend: + image: ${GITEA_REGISTRY:-192.168.68.53:3000}/kyle/vip-coordinator/backend:${IMAGE_TAG:-latest} + container_name: vip-coordinator-backend + environment: + # Database Configuration + DATABASE_URL: postgresql://${POSTGRES_USER:-vip_user}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-vip_coordinator} + + # Redis Configuration + REDIS_URL: redis://redis:6379 + + # Auth0 Configuration + AUTH0_DOMAIN: ${AUTH0_DOMAIN:?AUTH0_DOMAIN must be set} + AUTH0_AUDIENCE: ${AUTH0_AUDIENCE:?AUTH0_AUDIENCE must be set} + AUTH0_ISSUER: ${AUTH0_ISSUER:?AUTH0_ISSUER must be set} + + # Application Configuration + NODE_ENV: production + PORT: 3000 + + # Optional: AviationStack API (for flight tracking) + AVIATIONSTACK_API_KEY: ${AVIATIONSTACK_API_KEY:-} + + # Optional: Database seeding + RUN_SEED: ${RUN_SEED:-false} + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + networks: + - vip-coordinator-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/v1/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # React Frontend (Nginx) (from Gitea registry) + frontend: + image: ${GITEA_REGISTRY:-192.168.68.53:3000}/kyle/vip-coordinator/frontend:${IMAGE_TAG:-latest} + container_name: vip-coordinator-frontend + ports: + - "${FRONTEND_PORT:-80}:80" + depends_on: + backend: + condition: service_healthy + networks: + - vip-coordinator-network + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 5s + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + +# Named volumes for data persistence +volumes: + vip-coordinator-postgres-data: + name: vip-coordinator-postgres-data + vip-coordinator-redis-data: + name: vip-coordinator-redis-data + +# Dedicated network for service communication +networks: + vip-coordinator-network: + name: vip-coordinator-network + driver: bridge