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 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 20:09:48 +01:00
parent 689b89ea83
commit e9de71ce29
3 changed files with 642 additions and 0 deletions

46
.env.digitalocean.example Normal file
View File

@@ -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

459
DIGITAL_OCEAN_DEPLOYMENT.md Normal file
View File

@@ -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`

View File

@@ -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