CRTX
DocsSign in →

Self-hosting & deploying in your environment

This page covers running CRTX beyond a laptop — from the default managed deployment (Railway + Vercel) to running it inside a customer's own infrastructure (single-tenant, VPC, on-prem), with notes on bring-your-own-keys, data residency, and white-labeling.

CRTX has three runtime processes and four external dependencies:

Processes                         External services
─────────                         ─────────────────
1. API        (uvicorn/FastAPI)   Supabase   (Postgres + Auth + Storage)
2. Worker     (arq)               Pinecone   (vector index)
3. Frontend   (Next.js)           OpenAI     (LLM + vision + embeddings)
                                  Redis      (job queue / rate limiting)
                                  Cohere     (optional, reranking)

The single most important rule: the worker is not optional. It processes ingestion and async eval scoring. Any deployment must run process #2 alongside #1.


#Option A — Managed (default): Railway + Vercel

The reference deployment:

  • Backend (API + worker) → Railway. Deploy the backend/ service.
    • runtime.txt pins Python 3.13; requirements.txt installs deps.
    • Run two processes. The Procfile defines the web: process (the API). You must also run the worker as a separate Railway service/command: arq app.worker.WorkerSettings. If you skip it, uploads sit in queued forever and scores never appear. (This is called out in the scaling audit.)
    • Add a Redis plugin/service and set REDIS_URL.
    • Set all backend env vars from Configuration.
  • Frontend → Vercel. Deploy frontend/, set NEXT_PUBLIC_* env vars, point NEXT_PUBLIC_API_URL at the Railway backend URL, and add that Vercel domain to the backend's ALLOWED_ORIGINS.
  • Supabase + Pinecone are managed SaaS in this model.

#Option B — Containerized (portable): Docker Compose

To run CRTX as self-contained containers — the basis for VPC/on-prem and the listed roadmap item "Docker Compose for one-command local setup":

# docker-compose.yml (illustrative — adapt to your images)
services:
  redis:
    image: redis:7-alpine
    ports: ["6379:6379"]

  api:
    build: ./backend
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 2
    env_file: ./backend/app/.env
    depends_on: [redis]
    ports: ["8000:8000"]

  worker:                       # REQUIRED — ingestion + eval scoring
    build: ./backend
    command: arq app.worker.WorkerSettings
    env_file: ./backend/app/.env
    depends_on: [redis]

  frontend:
    build: ./frontend
    env_file: ./frontend/.env.local
    ports: ["3000:3000"]

Notes:

  • api and worker share the same image and env; only the command differs.
  • Run the API with --workers N for concurrency (see Operations).
  • Supabase/Pinecone are still external in this setup. To make it fully self-contained, see "Data residency" below.

#Option C — Single-tenant in a customer's environment (VPC / on-prem)

For customers who require CRTX to run in their own cloud account or data center:

  1. One isolated deployment per customer. Give each customer a dedicated stack (their own API+worker+Redis+DB+index). This is the cleanest data-isolation story and simplifies compliance ("your data never leaves your account").
  2. Bring-your-own-keys. Point the deployment at the customer's own Supabase project (or self-hosted Postgres/Storage), their own Pinecone (or a self-hosted vector store), and their own OpenAI/Azure OpenAI account. All of this is just env configuration (Configuration) — no code changes.
  3. Network posture. Run inside the customer's VPC; expose only the frontend (and, if used, the API-key surface) through their ingress/WAF. The worker and Redis stay private.
  4. Secrets. Load env from the customer's secret manager (AWS Secrets Manager, GCP Secret Manager, Vault) rather than a committed .env.
  5. Egress. By default CRTX makes outbound calls to OpenAI/Pinecone/Cohere. For restricted networks, allow-list those endpoints, or use in-region/self-hosted substitutes (below).

#Data residency & fully self-contained deployments

Depending on the customer's requirements, swap managed dependencies for in-region or self-hosted equivalents:

DependencyManaged defaultSelf-hosted / in-region optionEffort
LLM + embeddings + visionOpenAIAzure OpenAI (same models, regional/compliance controls) — configure the OpenAI client's base URL/keysLow (config/client)
Vector storePinecone (cloud)pgvector in the customer's Postgres, or a self-hosted vector DBMedium (a vector_store adapter)
Postgres + Auth + StorageSupabase (cloud)Self-hosted Supabase (it ships as containers) or split Postgres/GoTrue/object storageMedium (ops)
Job queueRedis (managed)Redis container in the same networkLow
RerankingCohere (cloud)Leave disabled, or a self-hosted cross-encoderLow

The abstractions that make this tractable: vector access is centralized in app/db/vector_store.py, auth is a single dependency in app/auth.py, and every provider is chosen by env. A pgvector swap is the largest lift (reimplement upsert/query/fetch against the same interface).


#White-labeling & multi-tenant SaaS

Two ways to serve multiple customers:

  • Multi-tenant (shared) SaaS — the current shape. One deployment serves all users; isolation is logical (per-user ownership + per-collection Pinecone namespaces). Fastest to operate; suitable when customers accept a shared control plane. Before scaling this, apply the fixes in the scaling audit (indexes, the list_users full-table read, uvicorn workers).
  • Single-tenant — Option C above; one stack per customer. Higher operational overhead, strongest isolation and residency story.

For white-labeling the UI, drive branding (logo, name, colors, legal links) from environment/config in the frontend so each deployment can be re-skinned without a code fork.


#Pre-flight checklist

  • API and worker both running.
  • REDIS_URL reachable from both.
  • Pinecone index is 1024-dim / cosine and matches EMBEDDING_DIMENSIONS.
  • Supabase documents (private) and images (public) buckets exist.
  • All application tables + observability tables created.
  • ALLOWED_ORIGINS includes the exact frontend origin.
  • Service-role key is server-side only; anon key only in the frontend.
  • Recommended DB indexes applied (see scaling audit).
  • Secrets loaded from a secret manager, not committed.
  • API run with --workers N (or multiple replicas) for production concurrency.

Next: Operations & scaling →