CyberOrigen DigitalOcean Deployment Guide
Complete checklist for deploying CyberOrigen to DigitalOcean instead of AWS.
Prerequisites
- [ ] DigitalOcean account (use referral link for $200 credit: https://digitalocean.com)
- [ ] Domain name with DNS access (e.g., Cloudflare, Namecheap)
- [ ] Stripe account (already have - just need to update webhook URLs)
- [ ] Anthropic API key (https://console.anthropic.com)
- [ ] Resend API key (already have)
Phase 1: Create DigitalOcean Resources
1.1 Create Droplet
- Go to https://cloud.digitalocean.com/droplets/new
- Choose:
- Region: NYC1 or closest to your users
- Image: Marketplace → Docker on Ubuntu 22.04
- Size: Basic → Regular → $24/mo (4GB RAM, 2 vCPUs, 80GB SSD)
- Authentication: SSH Key (recommended) or Password
- Hostname:
cyberorigen-prod
- Click "Create Droplet"
- Note the IP address:
_______________
1.2 Create Managed PostgreSQL (Recommended)
- Go to https://cloud.digitalocean.com/databases/new
- Choose:
- Engine: PostgreSQL 15
- Size: Basic → $15/mo (1GB RAM, 1 vCPU)
- Region: Same as Droplet
- Name:
cyberorigen-db
- Click "Create Database Cluster"
- Wait for provisioning (~5 minutes)
- Note connection details:
- Host:
_______________ - Port:
25060(default for managed DB) - Database:
defaultdb(or createcyberorigen) - User:
doadmin - Password:
_______________
- Host:
Alternative: Run PostgreSQL in Docker (saves $15/mo but less reliable)
1.3 Create Spaces Bucket (S3-Compatible Storage)
- Go to https://cloud.digitalocean.com/spaces/new
- Choose:
- Region: NYC3 (or same region as droplet)
- Name:
cyberorigen-evidence - File Listing: Restricted
- Click "Create Space"
- Go to API → Spaces Keys → Generate New Key
- Note:
- Access Key:
_______________ - Secret Key:
_______________ - Endpoint:
https://nyc3.digitaloceanspaces.com
- Access Key:
Phase 2: Configure DNS
2.1 Add DNS Records
In your DNS provider (Cloudflare, Namecheap, etc.):
| Type | Name | Value | TTL |
|---|---|---|---|
| A | backend | <DROPLET_IP> | Auto |
| A | app | <DROPLET_IP> | Auto |
| A | @ | <DROPLET_IP> | Auto (optional, for root domain) |
Example for cyberorigen.com:
backend.cyberorigen.com→ Droplet IPapp.cyberorigen.com→ Droplet IP
2.2 Wait for DNS Propagation
Check propagation: https://dnschecker.org
Phase 3: Server Setup
3.1 SSH into Droplet
bash
ssh root@<DROPLET_IP>3.2 Create App Directory
bash
mkdir -p /opt/cyberorigen
cd /opt/cyberorigen3.3 Clone Repository
bash
git clone https://github.com/YOUR_USERNAME/co-development.git .
# Or use deploy key for private repo3.4 Create Environment File
bash
nano .envPaste the following (fill in your values):
env
# ===========================================
# CyberOrigen Production Environment
# DigitalOcean Deployment
# ===========================================
# ---- Core Settings ----
ENVIRONMENT=production
DEBUG=false
SECRET_KEY=<generate-with: openssl rand -hex 32>
ENCRYPTION_KEY=<generate-with: python3 -c "import secrets; print(secrets.token_urlsafe(32))">
# ---- API Settings ----
API_HOST=0.0.0.0
API_PORT=8000
FRONTEND_URL=https://app.cyberorigen.com
# ---- Database (DigitalOcean Managed PostgreSQL) ----
POSTGRES_HOST=<your-db-host>.db.ondigitalocean.com
POSTGRES_PORT=25060
POSTGRES_DB=cyberorigen
POSTGRES_USER=doadmin
POSTGRES_PASSWORD=<your-db-password>
# ---- Redis ----
REDIS_URL=redis://redis:6379
# ---- AI Configuration ----
# CRITICAL: Set to false to use direct API instead of Bedrock
AI_TIER_ENFORCEMENT=false
ANTHROPIC_API_KEY=sk-ant-api03-<your-key>
GEMINI_API_KEY=<your-key>
OPENAI_API_KEY=<your-key>
# Default to Claude direct API
BEDROCK_MODEL_ID=claude-sonnet-4-20250514
# ---- Storage (DigitalOcean Spaces) ----
STORAGE_BACKEND=minio
MINIO_ENDPOINT=https://nyc3.digitaloceanspaces.com
MINIO_BUCKET=cyberorigen-evidence
MINIO_ACCESS_KEY=<your-spaces-access-key>
MINIO_SECRET_KEY=<your-spaces-secret-key>
MINIO_USE_SSL=true
# Quarantine storage (same bucket, different prefix)
QUARANTINE_S3_BUCKET=cyberorigen-evidence
QUARANTINE_S3_REGION=nyc3
# ---- Email (Resend) ----
RESEND_API_KEY=re_<your-key>
# ---- Stripe Billing ----
STRIPE_SECRET_KEY=sk_live_<your-key>
STRIPE_PUBLISHABLE_KEY=pk_live_<your-key>
STRIPE_WEBHOOK_SECRET=whsec_<your-key>
# ---- Security Scanning ----
MAX_CONCURRENT_SCANS=3
SCAN_TIMEOUT_MINUTES=120
# ---- Threat Scanning ----
THREAT_SCANNING_ENABLED=true
CLAMAV_HOST=clamav
CLAMAV_PORT=3310
# ---- PII Redaction ----
PII_REDACTION_ENABLED=true
PII_REDACTION_LOG_DETECTIONS=true
# ---- Peppermint Ticketing ----
PEPPERMINT_ENABLED=true
PEPPERMINT_API_URL=http://peppermint:5003
PEPPERMINT_UI_URL=https://tickets.cyberorigen.com
[email protected]
PEPPERMINT_ADMIN_PASSWORD=<strong-password>3.5 Create Docker Compose for Production
bash
nano docker-compose.prod.ymlyaml
version: '3.8'
services:
# ===========================================
# Backend API
# ===========================================
api:
build:
context: ./backend
dockerfile: Dockerfile
container_name: cyberorigen-api
restart: unless-stopped
env_file: .env
ports:
- "8000:8000"
volumes:
- ./backend:/app
- evidence_data:/app/evidence_storage
- quarantine_data:/app/quarantine_storage
depends_on:
- redis
- clamav
networks:
- cyberorigen
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
# ===========================================
# Frontend (Production Build)
# ===========================================
frontend:
build:
context: ./ui_cyberorigen
dockerfile: Dockerfile
container_name: cyberorigen-frontend
restart: unless-stopped
ports:
- "3000:80"
depends_on:
- api
networks:
- cyberorigen
# ===========================================
# Redis (Session & Cache)
# ===========================================
redis:
image: redis:7-alpine
container_name: cyberorigen-redis
restart: unless-stopped
volumes:
- redis_data:/data
networks:
- cyberorigen
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
# ===========================================
# ClamAV (Malware Scanning)
# ===========================================
clamav:
image: clamav/clamav:latest
container_name: cyberorigen-clamav
restart: unless-stopped
volumes:
- clamav_data:/var/lib/clamav
networks:
- cyberorigen
healthcheck:
test: ["CMD", "clamdscan", "--version"]
interval: 60s
timeout: 10s
retries: 3
# ===========================================
# Caddy (Reverse Proxy + Auto SSL)
# ===========================================
caddy:
image: caddy:2-alpine
container_name: cyberorigen-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- api
- frontend
networks:
- cyberorigen
volumes:
redis_data:
clamav_data:
evidence_data:
quarantine_data:
caddy_data:
caddy_config:
networks:
cyberorigen:
driver: bridge3.6 Create Caddyfile (Auto SSL)
bash
nano Caddyfile# Backend API
backend.cyberorigen.com {
reverse_proxy api:8000
# Security headers
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
# Frontend App
app.cyberorigen.com {
reverse_proxy frontend:3000
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
}
}
# Redirect www to non-www (optional)
www.cyberorigen.com {
redir https://cyberorigen.com{uri} permanent
}Phase 4: Deploy
4.1 Build and Start Containers
bash
cd /opt/cyberorigen
docker-compose -f docker-compose.prod.yml build
docker-compose -f docker-compose.prod.yml up -d4.2 Check Container Status
bash
docker-compose -f docker-compose.prod.yml ps
docker-compose -f docker-compose.prod.yml logs -f api4.3 Run Database Migrations
bash
docker-compose -f docker-compose.prod.yml exec api alembic upgrade head4.4 Create Platform Admin User
bash
docker-compose -f docker-compose.prod.yml exec api python -c "
from models.database import SessionLocal, User, Organization
from passlib.context import CryptContext
import uuid
pwd_context = CryptContext(schemes=['bcrypt'], deprecated='auto')
db = SessionLocal()
# Create org
org = Organization(
name='CyberOrigen Platform',
org_id=str(uuid.uuid4()),
subscription_tier='ENTERPRISE',
is_active=True
)
db.add(org)
db.commit()
# Create admin
admin = User(
email='[email protected]',
username='admin',
hashed_password=pwd_context.hash('<YOUR_ADMIN_PASSWORD>'),
is_platform_admin=True,
organization_id=org.id,
is_active=True
)
db.add(admin)
db.commit()
print(f'Admin created: {admin.email}')
db.close()
"Phase 5: Update External Services
5.1 Update Stripe Webhook
- Go to https://dashboard.stripe.com/webhooks
- Update webhook endpoint URL to:
https://backend.cyberorigen.com/api/billing/webhook - Note the new webhook secret and update
.env
5.2 Update Resend Domain
- Verify sending domain in Resend dashboard
- Ensure SPF/DKIM records are correct
5.3 Update Frontend API URL
In ui_cyberorigen/vite.config.ts, the proxy should point to production:
typescript
proxy: {
'/api': {
target: 'https://backend.cyberorigen.com',
// ...
}
}Phase 6: Testing Checklist
6.1 API Health
bash
curl https://backend.cyberorigen.com/health
# Expected: {"status": "healthy", ...}6.2 Authentication
bash
curl -X POST https://backend.cyberorigen.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "<password>"}'
# Expected: {"access_token": "...", ...}6.3 Frontend
- [ ] Open https://app.cyberorigen.com
- [ ] Login works
- [ ] Dashboard loads
- [ ] Scans can be created
- [ ] AI chat responds (using Anthropic direct API)
6.4 Stripe
- [ ] Billing page loads pricing
- [ ] Test checkout flow (use Stripe test mode first)
- [ ] Webhooks received (check Stripe dashboard)
6.5 Storage
- [ ] Upload evidence file
- [ ] File appears in DigitalOcean Spaces bucket
- [ ] Download works
Phase 7: Monitoring & Maintenance
7.1 View Logs
bash
# All services
docker-compose -f docker-compose.prod.yml logs -f
# Specific service
docker-compose -f docker-compose.prod.yml logs -f api7.2 Restart Services
bash
docker-compose -f docker-compose.prod.yml restart api7.3 Update Deployment
bash
cd /opt/cyberorigen
git pull origin main
docker-compose -f docker-compose.prod.yml build
docker-compose -f docker-compose.prod.yml up -d
docker-compose -f docker-compose.prod.yml exec api alembic upgrade head7.4 Backup Database
bash
# DigitalOcean Managed DB has automatic backups
# For manual backup:
pg_dump -h <host> -U doadmin -d cyberorigen > backup_$(date +%Y%m%d).sqlCost Summary
| Resource | Monthly Cost |
|---|---|
| Droplet (4GB) | $24 |
| Managed PostgreSQL | $15 |
| Spaces (250GB included) | $5 |
| Total | $44/mo |
Optional additions:
- Increase Droplet to 8GB: +$24 (total $48)
- Load balancer: +$12
- Managed Redis: +$15
Troubleshooting
Container won't start
bash
docker-compose -f docker-compose.prod.yml logs api
# Check for missing env vars or connection errorsDatabase connection refused
- Check POSTGRES_HOST is the full DO hostname
- Ensure Droplet IP is in database trusted sources
- Verify port is 25060 (not 5432)
SSL certificate errors
bash
docker-compose -f docker-compose.prod.yml logs caddy
# Caddy auto-provisions certs, check DNS is correctAI not responding
- Verify
AI_TIER_ENFORCEMENT=false - Check
ANTHROPIC_API_KEYis valid - Test key:
curl https://api.anthropic.com/v1/messages -H "x-api-key: $ANTHROPIC_API_KEY"
Spaces upload fails
- Check endpoint is
https://nyc3.digitaloceanspaces.com(with region) - Verify access key has write permissions
- Ensure
MINIO_USE_SSL=true
Quick Reference Commands
bash
# SSH to server
ssh root@<DROPLET_IP>
# Go to app directory
cd /opt/cyberorigen
# View running containers
docker ps
# View logs
docker-compose -f docker-compose.prod.yml logs -f
# Restart everything
docker-compose -f docker-compose.prod.yml restart
# Full rebuild
docker-compose -f docker-compose.prod.yml down
docker-compose -f docker-compose.prod.yml build --no-cache
docker-compose -f docker-compose.prod.yml up -d
# Run migrations
docker-compose -f docker-compose.prod.yml exec api alembic upgrade head
# Enter API container shell
docker-compose -f docker-compose.prod.yml exec api bashDocument Version: 1.0 Created: 2024-12-23 For: CyberOrigen GRC Platform