- 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>
460 lines
9.8 KiB
Markdown
460 lines
9.8 KiB
Markdown
# 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`
|