Skip to main content

Self-hosting & running the SaaS

LeonAI is a TypeScript monorepo: a Fastify API, a Next.js frontend, analytics and webhook workers, and shared packages. This page covers local development and production deployment on Google Cloud Platform (GCP).

Prerequisites​

  • Node.js 20+
  • Docker (for local MySQL 8 + Redis 7 via Docker Compose)

Local development​

# One-time setup: starts MySQL + Redis, writes .env files with generated dev
# secrets, installs deps, builds shared packages, runs Prisma migrations.
npm run setup

# Start everything: API on :8080, frontend on :3000, workers in-process.
npm run dev

# Seed a demo account (API must be running).
npm run seed:demo

npm run setup is idempotent. After seed:demo you can log in with:

  • Email: demo@leonai.test
  • Password: Demo12345 (Business tier)

Handy local URLs:

WhatURL
Dashboardhttp://localhost:3000
API healthhttp://localhost:8080/health
Swagger UIhttp://localhost:8787/api/v1/docs
Redirecthttp://localhost:8080/<shortCode>

Optional services (Sumit billing, SendGrid email, MaxMind GeoIP, Anthropic AI, Cloudflare domain auto-connect) degrade gracefully when their keys are absent — for example, AI falls back to the deterministic heuristic provider.

Environment variables​

Local defaults are written by npm run setup. For production the API refuses to boot unless the security-critical values are strong and explicit — this is enforced by the env-guard (packages/api/src/utils/env-guard.ts).

Hard-fail in production (the API will not start)​

VariableRequirement
JWT_SECRETPresent, ≥32 chars, not a known placeholder
COOKIE_SECRETPresent, ≥32 chars, distinct from JWT_SECRET
CORS_ORIGINPresent — explicit origin(s), comma-separated; no wildcard/permissive default

Also required for a working deployment​

VariablePurpose
DATABASE_URLCloudSQL MySQL (Unix socket on Cloud Run)
REDIS_* (host/port/password, read+write)Cache, rate limits, queues, sessions
TOTP_ENCRYPTION_KEY32-byte hex — required for 2FA enrollment (else 503)
DNS_ENCRYPTION_KEYEncrypts stored Cloudflare tokens for domain auto-connect (falls back to TOTP_ENCRYPTION_KEY)
APP_URL, SHORT_DOMAINFrontend origin and short-link domain

Feature-specific (optional)​

VariableEnables
SUMIT_COMPANY_ID, SUMIT_API_KEY, SUMIT_API_PUBLIC_KEY, SUMIT_WEBHOOK_SECRET, SUMIT_DOCUMENT_LANGUAGEBilling (Sumit)
SENDGRID_API_KEY / EMAIL_API_KEYVerification, invite, reset email
MAXMIND_LICENSE_KEYGeoIP city-level analytics
ANTHROPIC_API_KEYAI prose (omit → heuristic)

Other tunables: NODE_ENV, LOG_LEVEL (default info), JWT_EXPIRES_IN (default 7d), WORKERS_IN_PROCESS, PORT (default 8080), HOST (default 0.0.0.0).

Production on GCP​

Deployment is git-driven — pushing to a branch triggers Cloud Build (never run gcloud builds submit directly). Five Cloud Run (Gen2) services run: api, frontend, analytics-worker, webhook-worker, and (optionally) docs. In production the API runs with WORKERS_IN_PROCESS=false so the workers scale independently.

First-time setup:

  1. Create the GCP project and enable the required APIs.
  2. cd infrastructure/terraform && terraform init && terraform apply.
  3. Populate Secret Manager: infrastructure/scripts/init-secrets.sh.
  4. Run migrations: infrastructure/scripts/migrate-db.sh (wraps prisma migrate deploy).
  5. Connect the GitHub repo to a Cloud Build trigger, or run the GitHub Actions workflows .github/workflows/cd-staging.yml and cd-production.yml manually. They are workflow_dispatch only: LeonAI's own deployment runs on Cloudflare, so these ship as GCP templates rather than live triggers. Wire them to main → production and develop → staging yourself if you want them automatic.
  6. Attach custom domains with managed SSL.
  7. Point the Sumit webhook at https://api.<domain>/api/v1/billing/sumit-webhook?secret=<SUMIT_WEBHOOK_SECRET>.

Deploy:

# Staging
git push origin develop

# Production (blue-green)
git checkout main && git merge --ff-only develop && git push origin main

Roll back by shifting traffic to a previous revision (migrations are forward-only):

gcloud run services update-traffic <service> --to-revisions <prev-revision>=100

See QUICKSTART.md and docs/DEPLOYMENT-RUNBOOK.md in the repo for the full runbook.