How to deploy Retriever to production.
| Component | Platform | Method |
|---|---|---|
| Backend | Google Cloud Run | gcloud run deploy --source ./backend |
| Frontend | Cloudflare Pages | Git-connected or wrangler pages deploy |
| Database | Supabase | Managed Postgres + pgvector |
| Auth | Supabase Auth | Managed, JWKS endpoint for JWT verification |
| LLM Gateway | Cloudflare AI Gateway | Routes OpenRouter + OpenAI traffic |
- Google Cloud account with billing enabled
gcloudCLI configured and authenticated- Cloudflare account (for Pages + AI Gateway)
- Supabase project created
- API keys ready:
gcloud run deploy retriever \
--source ./backend \
--region us-central1 \
--project $PROJECT_ID \
--allow-unauthenticated \
--memory 1Gi \
--cpu 1 \
--min-instances 0 \
--max-instances 10 \
--timeout 60 \
--set-env-vars "DEBUG=false" \
--set-secrets "OPENROUTER_API_KEY=retriever-openrouter-api-key:latest,OPENAI_API_KEY=retriever-openai-api-key:latest,DATABASE_URL=retriever-database-url:latest"This builds the container from source using Cloud Build and deploys it in one step. No local Docker build required.
Create secrets before first deploy:
export PROJECT_ID=your-project-id
# Create secrets
echo -n "placeholder" | gcloud secrets create retriever-openrouter-api-key --data-file=- --project="$PROJECT_ID"
echo -n "placeholder" | gcloud secrets create retriever-openai-api-key --data-file=- --project="$PROJECT_ID"
echo -n "placeholder" | gcloud secrets create retriever-database-url --data-file=- --project="$PROJECT_ID"
# Update with real values
echo -n 'sk-or-v1-your-key' | gcloud secrets versions add retriever-openrouter-api-key --data-file=- --project="$PROJECT_ID"
echo -n 'sk-your-key' | gcloud secrets versions add retriever-openai-api-key --data-file=- --project="$PROJECT_ID"
echo -n 'postgresql+asyncpg://...' | gcloud secrets versions add retriever-database-url --data-file=- --project="$PROJECT_ID"
# Grant Cloud Run access
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
for SECRET in retriever-openrouter-api-key retriever-openai-api-key retriever-database-url; do
gcloud secrets add-iam-policy-binding $SECRET \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor" \
--project="$PROJECT_ID"
doneSERVICE_URL=$(gcloud run services describe retriever --region us-central1 --project $PROJECT_ID --format="value(status.url)")
curl $SERVICE_URL/healthSee Cloud Run Deployment Guide for detailed step-by-step instructions.
- Go to Cloudflare Dashboard > Pages
- Connect your GitHub repository
- Configure build settings:
- Build command:
npm run build - Build output directory:
.svelte-kit/cloudflare - Root directory:
frontend
- Build command:
- Add environment variables:
PUBLIC_SUPABASE_URLPUBLIC_SUPABASE_ANON_KEYPUBLIC_API_BASE_URL(Cloud Run backend URL)
cd frontend
npm run build
npx wrangler pages deploy .svelte-kit/cloudflare --project-name=retriever- Create a Supabase project at supabase.com
- Enable pgvector extension: SQL Editor >
CREATE EXTENSION IF NOT EXISTS vector; - Run Alembic migrations against the Supabase database:
cd backend DATABASE_URL="postgresql+asyncpg://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres" \ uv run alembic upgrade head
- Configure RLS policies for the
messages,documents, anduserstables
Use the Supabase connection pooler URL (port 6543) for the DATABASE_URL environment variable. Format:
postgresql+asyncpg://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | Supabase Postgres connection string (asyncpg) |
OPENROUTER_API_KEY |
Yes | OpenRouter API key for LLM |
OPENAI_API_KEY |
Yes | OpenAI API key for embeddings |
SUPABASE_URL |
Yes | Supabase project URL |
SUPABASE_ANON_KEY |
Yes | Supabase anonymous key |
CLOUDFLARE_ACCOUNT_ID |
No | Cloudflare account ID (enables AI Gateway) |
CLOUDFLARE_GATEWAY_ID |
No | Cloudflare gateway ID (enables AI Gateway) |
LANGFUSE_SECRET_KEY |
No | Langfuse secret key (LLM observability) |
LANGFUSE_PUBLIC_KEY |
No | Langfuse public key |
LANGFUSE_HOST |
No | Langfuse host URL |
GCP_PROJECT_ID |
No | GCP project (enables Cloud Trace exporter) |
ENVIRONMENT |
No | development or production |
LOG_LEVEL |
No | DEBUG, INFO, WARNING, ERROR |
- Supabase project created with pgvector extension enabled
- Alembic migrations applied to Supabase database
- RLS policies configured for all tables
- GCP Secret Manager secrets created and populated
- Backend deployed to Cloud Run:
gcloud run deploy --source ./backend - Frontend deployed to Cloudflare Pages
- Health check responds:
GET /health - Admin user created in Supabase Auth dashboard
- Can log in and ask a question
- SSL certificates active (automatic on both platforms)
- Cloudflare AI Gateway configured (optional but recommended)
- Langfuse configured for LLM observability (optional)
- GCP Cloud Trace enabled for distributed tracing (optional)
GET /health → Liveness + DB + pgvector checks
# Cloud Run logs
gcloud run services logs read retriever --region us-central1 --project $PROJECT_ID --limit 50- Structured logs: JSON via structlog (visible in Cloud Run logs)
- Distributed tracing: GCP Cloud Trace (if
GCP_PROJECT_IDset) or Jaeger (ifOTEL_EXPORTER_OTLP_ENDPOINTset) - LLM observability: Langfuse (if credentials configured)
Cloud Run auto-scales from 0 to max-instances. For MVP scale (50-100 volunteers), default settings are sufficient.
If needed:
- Increase
--memoryand--cpubefore adding instances - Enable Supabase connection pooling (PgBouncer) if connection limits hit
- See Future: Production Hardening
Backend (Cloud Run):
# List revisions
gcloud run revisions list --service retriever --region us-central1 --project $PROJECT_ID
# Route traffic to previous revision
gcloud run services update-traffic retriever --to-revisions=REVISION_NAME=100 --region us-central1 --project $PROJECT_IDFrontend (Cloudflare Pages): Roll back via the Cloudflare Pages dashboard > Deployments > select previous deployment > "Rollback to this deploy".