Deployment
Triple-deploy architecture explained
Deploy Offworld using the triple-deploy architecture.
Triple-Deploy Architecture
Offworld deploys to three separate platforms:
- Backend → Convex Cloud (serverless database + functions)
- Frontend → Cloudflare Workers via Alchemy (edge SSR)
- Docs → Netlify (static site)
Why Three Platforms?
Convex Cloud - Best for:
- Real-time database with subscriptions
- Serverless functions (queries, mutations, actions)
- Durable workflows
- Vector search (RAG)
Cloudflare Workers - Best for:
- Edge SSR (fast global routing)
- <50ms cold starts
- Global CDN
- Low cost
Netlify - Best for:
- Static site hosting
- Branch previews
- Instant rollbacks
- Simple deployments
Backend Deployment (Convex Cloud)
Deploy Backend
bun run deploy:backendThis runs:
npx convex deploy --yesWhat happens:
- Pushes functions to Convex Cloud
- Applies schema migrations
- Deploys to production deployment
- Generates new API URL
Setup Production Env Vars
Set in Convex Dashboard or via CLI:
npx convex env set CONVEX_SITE_URL https://offworld.sh --prod
npx convex env set GITHUB_CLIENT_ID your_prod_client_id --prod
npx convex env set GITHUB_CLIENT_SECRET your_prod_secret --prod
npx convex env set GITHUB_APP_ID your_app_id --prod
npx convex env set GITHUB_PRIVATE_KEY "-----BEGIN..." --prod
npx convex env set GITHUB_INSTALLATION_ID your_install_id --prod
npx convex env set GOOGLE_GENERATIVE_AI_API_KEY your_key --prodUse --prod flag for production deployment (not dev).
Verify Backend
- Check Convex Dashboard → Production deployment
- Verify functions are deployed (Data, Functions tabs)
- Test a query: Click function → Run with test input
Frontend Deployment (Cloudflare via Alchemy)
Setup Alchemy
Install Alchemy CLI:
bun add -g @alchemy-framework/cliLogin:
alchemy loginConfigure Alchemy
File: apps/web/alchemy.run.ts
import { defineProject } from "@alchemy-framework/core";
export default defineProject({
name: "offworld-web",
domains: ["offworld.sh"],
build: {
command: "bun run build",
outputDir: ".output"
},
env: {
VITE_CONVEX_URL: process.env.VITE_CONVEX_URL,
VITE_CONVEX_SITE_URL: process.env.VITE_CONVEX_SITE_URL,
VITE_SENTRY_DSN: process.env.VITE_SENTRY_DSN
}
});Create alchemy.env
File: apps/web/alchemy.env (not committed):
VITE_CONVEX_URL=https://your-prod-deployment.convex.cloud
VITE_CONVEX_SITE_URL=https://offworld.sh
VITE_SENTRY_DSN=https://[email protected]/projectDeploy Frontend
bun run deploy:webThis runs:
cd apps/web && bun run deployWhich executes:
alchemy deployWhat happens:
- Builds frontend with TanStack Start
- Bundles for Cloudflare Workers
- Uploads to Cloudflare
- Binds environment variables
- Maps to domain (offworld.sh)
Verify Frontend
- Visit https://offworld.sh
- Test sign-in flow
- Analyze a repository
- Check Cloudflare dashboard for metrics
Docs Deployment (Netlify)
Setup Netlify
File: apps/fumadocs/netlify.toml
[build]
command = "turbo run build --filter fumadocs"
publish = "apps/fumadocs/.next"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200Deploy Docs
bun run deploy:docsOr push to GitHub - Netlify auto-deploys.
Configure Netlify
- Connect GitHub repo to Netlify
- Set build command:
turbo run build --filter fumadocs - Set publish directory:
apps/fumadocs/.next - Add custom domain (optional)
Verify Docs
Visit docs URL (e.g., offworld-docs.netlify.app).
All-in-One Deployment
Deploy everything sequentially:
bun run deploy:allRuns:
deploy:backend(Convex)deploy:web(Cloudflare)deploy:docs(Netlify)
Always deploy backend before frontend to ensure latest functions are available.
Environment Variables Summary
Backend (Convex)
Set in Convex Dashboard or via npx convex env set:
CONVEX_SITE_URL- Frontend URL (for auth)GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRET- OAuthGITHUB_APP_ID,GITHUB_PRIVATE_KEY,GITHUB_INSTALLATION_ID- GitHub APIGOOGLE_GENERATIVE_AI_API_KEY- Gemini AI
Frontend (Cloudflare)
Set in alchemy.env:
VITE_CONVEX_URL- Convex deployment URLVITE_CONVEX_SITE_URL- Frontend URL (for auth)VITE_SENTRY_DSN- Error tracking
Docs (Netlify)
No env vars needed (static site).
Domain Configuration
Cloudflare Domain (offworld.sh)
DNS Setup:
- Point domain to Cloudflare nameservers
- Alchemy automatically configures Workers route
SSL:
- Automatic via Cloudflare SSL
Custom Docs Domain
In Netlify:
- Dashboard → Domain settings
- Add custom domain
- Configure DNS (CNAME to Netlify)
Monitoring & Logs
Backend (Convex)
Convex Dashboard → Production deployment:
- Logs - Function execution logs
- Workflows - Workflow runs
- Data - Database tables
- Metrics - Request counts, latency
Frontend (Cloudflare)
Cloudflare Dashboard → Workers:
- Analytics - Request volume, latency
- Logs - Real-time logs (requires log push)
Sentry:
- Frontend error tracking
- Performance monitoring
- Release tracking
Docs (Netlify)
Netlify Dashboard:
- Deploys - Build history
- Analytics - Page views (optional)
Rollback
Backend Rollback
Convex doesn't support automatic rollback. To revert:
- Revert git commit with function changes
- Run
bun run deploy:backend
Frontend Rollback
Cloudflare:
alchemy rollbackOr redeploy previous git commit.
Docs Rollback
Netlify:
- Dashboard → Deploys
- Click previous deploy → Publish
CI/CD (Optional)
GitHub Actions
File: .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bun run deploy:backend
env:
CONVEX_DEPLOY_KEY: ${{ secrets.CONVEX_DEPLOY_KEY }}
deploy-frontend:
needs: deploy-backend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
- run: bun install
- run: bun run deploy:web
env:
ALCHEMY_API_KEY: ${{ secrets.ALCHEMY_API_KEY }}Next Steps
- Learn Contributing guidelines
- Check Troubleshooting for common issues