Skip to content

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

  1. Go to https://cloud.digitalocean.com/droplets/new
  2. 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
  3. Click "Create Droplet"
  4. Note the IP address: _______________
  1. Go to https://cloud.digitalocean.com/databases/new
  2. Choose:
    • Engine: PostgreSQL 15
    • Size: Basic → $15/mo (1GB RAM, 1 vCPU)
    • Region: Same as Droplet
    • Name: cyberorigen-db
  3. Click "Create Database Cluster"
  4. Wait for provisioning (~5 minutes)
  5. Note connection details:
    • Host: _______________
    • Port: 25060 (default for managed DB)
    • Database: defaultdb (or create cyberorigen)
    • User: doadmin
    • Password: _______________

Alternative: Run PostgreSQL in Docker (saves $15/mo but less reliable)

1.3 Create Spaces Bucket (S3-Compatible Storage)

  1. Go to https://cloud.digitalocean.com/spaces/new
  2. Choose:
    • Region: NYC3 (or same region as droplet)
    • Name: cyberorigen-evidence
    • File Listing: Restricted
  3. Click "Create Space"
  4. Go to API → Spaces Keys → Generate New Key
  5. Note:
    • Access Key: _______________
    • Secret Key: _______________
    • Endpoint: https://nyc3.digitaloceanspaces.com

Phase 2: Configure DNS

2.1 Add DNS Records

In your DNS provider (Cloudflare, Namecheap, etc.):

TypeNameValueTTL
Abackend<DROPLET_IP>Auto
Aapp<DROPLET_IP>Auto
A@<DROPLET_IP>Auto (optional, for root domain)

Example for cyberorigen.com:

  • backend.cyberorigen.com → Droplet IP
  • app.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/cyberorigen

3.3 Clone Repository

bash
git clone https://github.com/YOUR_USERNAME/co-development.git .
# Or use deploy key for private repo

3.4 Create Environment File

bash
nano .env

Paste 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.yml
yaml
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: bridge

3.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 -d

4.2 Check Container Status

bash
docker-compose -f docker-compose.prod.yml ps
docker-compose -f docker-compose.prod.yml logs -f api

4.3 Run Database Migrations

bash
docker-compose -f docker-compose.prod.yml exec api alembic upgrade head

4.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

  1. Go to https://dashboard.stripe.com/webhooks
  2. Update webhook endpoint URL to: https://backend.cyberorigen.com/api/billing/webhook
  3. Note the new webhook secret and update .env

5.2 Update Resend Domain

  1. Verify sending domain in Resend dashboard
  2. 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 api

7.2 Restart Services

bash
docker-compose -f docker-compose.prod.yml restart api

Use the deployment scripts from your local development machine:

bash
cd /home/d4sh010101/co-development/infrastructure/digitalocean/docker

Quick Reference

Change TypeCommand
Backend only./deploy-backend.sh
UI only./deploy-ui.sh
Both./deploy-all.sh
Monitoring./deploy-monitoring.sh
DefectDojo./deploy-defectdojo.sh

Standard Update Workflow

bash
# 1. Make changes locally
cd /home/d4sh010101/co-development

# 2. Test locally (optional)
cd backend && python -m pytest
cd ui_cyberorigen && npm run build

# 3. Commit changes
git add -A && git commit -m "description"

# 4. Deploy (from infrastructure/digitalocean/docker/)
cd infrastructure/digitalocean/docker

# Backend changes:
./deploy-backend.sh

# UI changes:
./deploy-ui.sh

# Both:
./deploy-all.sh

# 5. Push to remote
git push

What Each Script Does

deploy-backend.sh

  1. Pulls latest code from git
  2. Builds Docker image locally (with cache bust)
  3. Compresses and transfers image to droplet via SCP
  4. Loads image and recreates api container
  5. Runs health check to verify deployment

deploy-ui.sh

  1. Pulls latest code from git
  2. Runs npm run build locally
  3. Builds Docker image with production env vars baked in
  4. Transfers to droplet
  5. Recreates ui container

Zero-Downtime Deployment

  • Containers restart individually (--force-recreate api or ui)
  • Other services (Redis, ClamAV, Peppermint) stay running
  • Health checks verify success before script completes
  • Rollback is immediate if health check fails

Rollback Procedure

bash
# SSH to droplet
ssh -i ~/.ssh/digitalocean_cyberorigen [email protected]

# List available images
docker images

# Rollback to previous image (if tagged)
cd /opt/cyberorigen
docker compose -f docker-compose.app.yml up -d --force-recreate api

Legacy Method (Direct on Server)

If you need to deploy directly on the server:

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 head

7.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).sql

Cost Summary

ResourceMonthly 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 errors

Database 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 correct

AI not responding

  • Verify AI_TIER_ENFORCEMENT=false
  • Check ANTHROPIC_API_KEY is 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 bash

Document Version: 1.1 Created: 2024-12-23 Updated: 2026-01-04 For: CyberOrigen GRC Platform

Updated at:

Agentic AI-Powered Security & Compliance