- 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>
9.8 KiB
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
- Go to Digital Ocean
- Choose Image: Ubuntu 24.04 LTS x64
- Choose Size: Select based on recommendations above
- Choose Region: Select closest region
- Authentication: SSH keys (recommended) or password
- Hostname:
vip-coordinator - Tags:
production,vip-coordinator - Backups: Enable weekly backups (recommended)
- Click Create Droplet
Step 2: Initial Server Setup
SSH into Droplet
ssh root@YOUR_DROPLET_IP
Update System
apt update && apt upgrade -y
Create Non-Root User
adduser vipcoord
usermod -aG sudo vipcoord
usermod -aG docker vipcoord # Will add docker group later
Configure Firewall (UFW)
# 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
# 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:
# 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:
-
Tailscale VPN (Recommended)
- Install Tailscale on both your local machine and Digital Ocean droplet
- Access Gitea via Tailscale IP
-
SSH Tunnel
# On your local machine ssh -L 3000:192.168.68.53:3000 root@YOUR_DROPLET_IP -
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:
# 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
# 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
# On droplet
cd /home/vipcoord
# Copy and edit environment file
cp .env.digitalocean.example .env.digitalocean
nano .env.digitalocean
Update these values:
# 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
# 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:
nano Caddyfile
your-domain.com {
reverse_proxy localhost:80
}
Run Caddy:
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:
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:
- Go to Auth0 Dashboard
- Select your application
- Allowed Callback URLs: Add
https://your-domain.com - Allowed Web Origins: Add
https://your-domain.com - Allowed Logout URLs: Add
https://your-domain.com - Click Save Changes
Step 8: Database Backups
Automated Daily Backups
Create backup script:
nano /home/vipcoord/backup-db.sh
#!/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:
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
# 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
docker ps
docker compose -f docker-compose.digitalocean.yml ps
Monitor Resources
# Real-time resource usage
docker stats
# Disk usage
df -h
docker system df
Step 10: Updating Application
When you push new images to Gitea:
# 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
- Check firewall:
ufw status - Check containers:
docker ps - Check logs:
docker compose logs -f - Check Auth0 callback URLs match your domain
Database Connection Issues
# 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
# 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
- Set up domain name (optional but recommended)
- Configure monitoring (Uptime Robot, etc.)
- Set up log aggregation (Digital Ocean Monitoring, Papertrail)
- Configure automated updates
- Add staging environment for testing
Deployment completed! 🚀
Your VIP Coordinator is now live at https://your-domain.com