Deployment Guide
Production deployment guide for Vows Social AI.
Deployment Architecture
Stack: - Cloudflare Workers → API & Orchestrator - Fly.io → Python ML services - Qdrant Cloud → Vector database - Supabase → PostgreSQL - Vercel → Frontend (optional)
Prerequisites
Required Accounts
- Cloudflare Account (Workers, Durable Objects)
- Fly.io Account (ML inference)
- Qdrant Cloud Account (Vector DB)
- Supabase Account (PostgreSQL)
- GitHub Account (CI/CD)
Required Tools
# Install wrangler (Cloudflare CLI)
npm install -g wrangler
# Install flyctl (Fly.io CLI)
brew install flyctl # macOS
# OR
curl -L https://fly.io/install.sh | sh # Linux
# Install supabase CLI
brew install supabase/tap/supabase # macOS
# Install gh (GitHub CLI)
brew install gh
Environment Setup
1. Cloudflare Workers
Initialize Project:
Configure wrangler.toml:
name = "vows-orchestrator"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[durable_objects]
bindings = [
{ name = "ORCHESTRATOR", class_name = "OrchestratorDO" }
]
[[migrations]]
tag = "v1"
new_classes = ["OrchestratorDO"]
[vars]
ENVIRONMENT = "production"
[[kv_namespaces]]
binding = "CACHE"
id = "your-kv-namespace-id"
[[r2_buckets]]
binding = "MODELS"
bucket_name = "vows-models"
Set Secrets:
wrangler secret put QDRANT_API_KEY
wrangler secret put QDRANT_URL
wrangler secret put SUPABASE_URL
wrangler secret put SUPABASE_KEY
wrangler secret put OPENAI_API_KEY
wrangler secret put GEMINI_API_KEY
Deploy:
2. Fly.io ML Service
Initialize Fly App:
Configure fly.toml:
app = "vows-ml-inference"
primary_region = "syd" # Sydney
[build]
dockerfile = "Dockerfile"
[env]
PORT = "8080"
[[services]]
internal_port = 8080
protocol = "tcp"
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[[services.http_checks]]
interval = 10000
timeout = 2000
path = "/health"
[compute]
cpu_kind = "shared"
cpus = 1
memory_mb = 256
Set Secrets:
Deploy:
3. Qdrant Cloud
Create Cluster: 1. Go to https://cloud.qdrant.io 2. Create new cluster (1GB free tier) 3. Note the URL and API key
Initialize Collections:
curl -X PUT 'https://your-cluster.qdrant.io/collections/content_embeddings' \
-H 'api-key: your-api-key' \
-H 'Content-Type: application/json' \
-d '{
"vectors": {
"size": 384,
"distance": "Cosine"
}
}'
curl -X PUT 'https://your-cluster.qdrant.io/collections/user_embeddings' \
-H 'api-key: your-api-key' \
-H 'Content-Type: application/json' \
-d '{
"vectors": {
"size": 384,
"distance": "Cosine"
}
}'
4. Supabase
Create Project: 1. Go to https://supabase.com 2. Create new project 3. Note the URL and anon key
Run Migrations:
cd migrations
psql $DATABASE_URL < 001_initial_schema.sql
psql $DATABASE_URL < 002_interactions.sql
psql $DATABASE_URL < 003_collections.sql
Or using Supabase CLI:
CI/CD Pipeline
GitHub Actions Workflow
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm test
deploy-workers:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npx wrangler deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
deploy-ml-service:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Deployment Stages
1. Staging (on PR): - Deploy to preview environment - Run integration tests - Manual QA approval
2. Production (on merge to main): - Run full test suite - Deploy to production - Run smoke tests - Monitor metrics
Database Migrations
Migration Files
-- migrations/001_initial_schema.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
wedding_date DATE,
location JSONB,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE interactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
content_id TEXT NOT NULL,
action TEXT NOT NULL,
duration FLOAT,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_interactions_user ON interactions(user_id);
CREATE INDEX idx_interactions_content ON interactions(content_id);
Running Migrations
Manually:
With Supabase CLI:
Rollback:
Monitoring & Observability
Cloudflare Analytics
// Track metrics
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const start = Date.now();
try {
const response = await handleRequest(request, env);
// Log metrics
ctx.waitUntil(
env.ANALYTICS.writeDataPoint({
blobs: ['feed-generation'],
doubles: [Date.now() - start],
indexes: [request.headers.get('user-agent')]
})
);
return response;
} catch (error) {
// Log error
ctx.waitUntil(logError(error, env));
throw error;
}
}
};
Prometheus Metrics (Fly.io)
from prometheus_client import Counter, Histogram, generate_latest
# Metrics
requests_total = Counter('requests_total', 'Total requests')
request_duration = Histogram('request_duration_seconds', 'Request duration')
@app.get('/metrics')
async def metrics():
return Response(generate_latest(), media_type='text/plain')
Sentry Error Tracking
import * as Sentry from '@sentry/cloudflare';
Sentry.init({
dsn: env.SENTRY_DSN,
environment: env.ENVIRONMENT,
tracesSampleRate: 0.1
});
export default {
async fetch(request, env, ctx) {
try {
return await handleRequest(request, env);
} catch (error) {
Sentry.captureException(error);
throw error;
}
}
};
Deployment Checklist
Pre-Deployment
- All tests passing
- Code reviewed and approved
- Database migrations ready
- Environment variables configured
- Secrets set in all services
- Monitoring dashboards configured
- Backup strategy in place
Deployment
- Run database migrations
- Deploy ML service (Fly.io)
- Deploy Workers (Cloudflare)
- Verify deployments successful
- Run smoke tests
- Check error rates
Post-Deployment
- Monitor latency metrics
- Check error logs
- Verify user flows working
- Monitor cost usage
- Update changelog
- Notify team
Rollback Procedure
Quick Rollback (Workers)
# List deployments
wrangler deployments list
# Rollback to previous
wrangler rollback --message "Rolling back due to issue"
Rollback ML Service
Database Rollback
Health Checks
Workers Health Endpoint
app.get('/health', async (c) => {
const checks = {
qdrant: await checkQdrant(c.env),
supabase: await checkSupabase(c.env),
mlService: await checkMLService(c.env)
};
const healthy = Object.values(checks).every(c => c.status === 'ok');
return c.json({
status: healthy ? 'healthy' : 'degraded',
checks,
timestamp: new Date().toISOString()
}, healthy ? 200 : 503);
});
Monitoring Health
# Check Workers
curl https://vows-orchestrator.workers.dev/health
# Check ML Service
curl https://vows-ml-inference.fly.dev/health
Scaling
Cloudflare Workers
- Automatically scales
- No configuration needed
- Pay per request
Fly.io Scaling
# Scale to 3 instances
fly scale count 3
# Scale memory
fly scale memory 512
# Auto-scaling
fly autoscale set min=1 max=10
Qdrant Scaling
- Upgrade cluster size in dashboard
- $49/month for 5GB
- $99/month for 10GB
Supabase Scaling
- Pro tier: $25/month
- Increased storage and bandwidth
- Better performance
Cost Monitoring
Expected Costs (Production)
Free Tier (0-500 users): $0/month
Early Scale (500-2K users): ~$109/month - Cloudflare: $25 - Qdrant: $49 - Supabase: $25 - Fly.io: $10
Growth (2K-10K users): ~$449/month - Cloudflare: $175 - Qdrant: $99 - Supabase: $25 - Fly.io: $50 - AI/ML: $100
Cost Alerts
# Set up billing alerts in each service
# Cloudflare: Dashboard → Billing → Alerts
# Fly.io: fly billing alerts set 100
# Monitor daily in dashboards