Run Klu in production
The checked-in Klu application has three runtime processes: the Next.js web application, the Fastify data API, and a Graphile Worker process. PostgreSQL stores application records and retrieval Documents, Redis supplies caches and streaming state, and external services provide object storage, authentication, notifications, billing, collaboration, model routing, and embeddings.
This guide describes repository behavior and configuration. It does not confirm the topology, secrets, capacity, or release state of any live Klu deployment.
Runtime boundaries
| Component | Command | Default local address | Responsibility |
|---|---|---|---|
| Web | npm run dev | http://localhost:3000 | Workspace UI, NextAuth, and Next.js API routes. |
| Data API | npm run dev:api | http://localhost:3001 | Authenticated /v1 API, streaming routes, file proxy, and optional OpenAPI UI. |
| Worker | npm run dev:worker | No HTTP listener | Graphile jobs, scheduled tasks, Context indexing, async Actions, evals, imports, exports, and fine-tune synchronization. |
Run all three for full product behavior. The checked-in Procfile.dev also starts tier -v serve for local billing and metering workflows.
Prepare a local operator environment
The root package requires Node.js 20.10.0 or later and pins Node 20.10.0 with Volta. PostgreSQL must support pgvector.
From the application repository:
npm install
cp .env.example .env
Configure .env before starting a process. The web environment schema rejects missing, empty, or invalid required values at startup. SKIP_ENV_VALIDATION bypasses that guard; reserve it for tightly controlled diagnostics because later code can still fail when it reads an absent value.
Configure the data stores
Klu uses two Prisma schemas:
DATABASE_URLpoints to the main application database for users, workspaces, Apps, Actions, data, job metadata, and configuration.DOCUMENTS_DATABASE_URLpoints to the documents database for Context chunks and vector embeddings.
The URLs can point to separate databases or to one PostgreSQL database when both schemas can coexist. Enable vector for the documents schema. The main Prisma schema also declares vector and uuid-ossp.
GRAPHILE_DATABASE_URL is the Graphile Worker queue connection. It can use the main database, as shown in .env.example, or another database provisioned for the queue. The web/API job producer and worker must use the same queue database.
Apply main migrations with the checked-in Prisma schema:
npx prisma migrate deploy --schema prisma/schema.prisma
Apply every prisma/docs_migrations/*/migration.sql file to DOCUMENTS_DATABASE_URL in filename order. The checked-in npm run migrate:docs wrapper is interactive and its control flow does not provide a reliable unattended deployment path. Use an operator-reviewed migration step that stops on the first psql error.
Configure required services
The authoritative required-key list is src/env.mjs. The primary groups are:
- Authentication and mail:
NEXTAUTH_SECRET,NEXTAUTH_URL, Google and GitHub client credentials, andEMAIL_SERVER_*plusEMAIL_FROM. - Storage and caching:
AWS_ACCESS_KEY,AWS_SECRET_KEY,AWS_S3_BUCKET_NAME,AWS_S3_REGION,REDIS_URL, andENGINE_CACHE_PREFIX. - API and model execution:
NEXT_PUBLIC_API_URL,GATEWAY_URL,KLU_API_KEY,KLU_USER_ID, Azure gateway values,OPENAI_API_KEY, and pairedKLU_EMBEDDING_API_BASE_0…_9andKLU_EMBEDDING_API_KEY_0…_9values. - Jobs:
GRAPHILE_DATABASE_URLandGRAPHILE_CONCURRENT_JOBS. - Product integrations: Nango, Novu, Liveblocks, Tier, Segment, and Sentry values declared in the schema.
.env.example is useful as a starting point, though it does not enumerate every required embedding slot from the current schema. Compare the finished file with src/env.mjs before deployment.
Start the services
Use separate terminals during local development:
npm run dev
npm run dev:api
npm run dev:worker
The production entry points are npm run start:app, npm run start:api, and npm run start:worker. Build the web application before start:app. The API and worker execute TypeScript through their workspace start scripts and load the root .env file.
The API listens on PORT or port 3001 when PORT is absent. It binds to 0.0.0.0. The web application uses Next.js defaults unless its environment overrides them.
Check health and readiness
Both HTTP services expose /api/healthcheck:
curl --fail --silent --show-error http://localhost:3000/api/healthcheck
curl --fail --silent --show-error http://localhost:3001/api/healthcheck
An HTTP 200 proves that the corresponding process can serve that route. The handlers do not query PostgreSQL, Redis, the queue, object storage, the gateway, or the embedding service. Add dependency-aware probes in your deployment platform when you need deeper readiness evidence.
In development, the data API serves generated OpenAPI documentation at http://localhost:3001/docs. Production enables it only when API_ENABLE_SWAGGER_DOCS or API_ENABLE_SWAGGER has a true-like value.
Verify the worker through logs and an end-to-end job. A practical smoke test is to index a small Context source and confirm Ready for Search, or run an asynchronous Action and confirm its data record reaches a terminal result.
Operate asynchronous jobs
The web and API enqueue work through Graphile Worker. The worker polls every second and runs one job concurrently unless GRAPHILE_CONCURRENT_JOBS is set. Increase concurrency only after checking database connections, provider rate limits, memory, and job-specific fan-out.
Repository job creation defaults to maxAttempts: 1. Several cron tasks also set one attempt explicitly. A transient provider or database error therefore commonly requires a new job or a product-level retry. Do not assume queue-level automatic retries.
Batch jobs begin as running, become completed when their performed count reaches the target count, and become failed when the worker reports a job error. Batch metadata records error text, job ID, and task name. Context source processing has an additional source status; use that source status because the processor catches indexing errors after recording FAILED.
The worker's checked-in crontab uses UTC and schedules fine-tune synchronization, dataset token-count synchronization, OpenAI file synchronization, and curator runs. Review worker/src/crontab before changing deployment replicas: Graphile's job locking coordinates workers, while upstream API quotas and job cost still constrain safe parallelism.
Configure API boundaries
The Fastify API defaults to a 40 MiB request-body limit. Override it with the first valid positive value among:
API_BODY_LIMIT_BYTESorAPI_MAX_BODY_SIZE_BYTESAPI_BODY_LIMIT_MBorAPI_MAX_BODY_SIZE_MB
Byte settings take precedence over megabyte settings. Invalid or non-positive values fall back to 40 MiB.
Set API_CORS_ALLOWED_ORIGINS or CORS_ALLOWED_ORIGINS to a comma-separated list of HTTP or HTTPS origins. The API fails startup when the variable is present and contains no valid origin. When neither variable is set, the checked-in API uses Fastify CORS with origin: true, so production operators should set an explicit allowlist.
Request logs include a generated request ID, method, path without query parameters, status code, and response time. The API and worker redact common bearer tokens, API keys, signed query values, and credential-like URL parameters in their explicit error summaries. This is targeted redaction rather than a general guarantee that every application log is free of sensitive data. Avoid logging raw request bodies and audit new log fields.
Understand the checked-in deployment topology
flightcontrol.json defines production and staging environments in us-east-1. Each environment deploys the web, API, and worker separately and attaches health checks to the two HTTP services. Production uses main; staging uses staging.
Watch paths establish independent release boundaries:
- Web watches the repository while excluding
api/**/*andworker/**/*. - Production API watches
api/**/*,src/server/**/*, andsrc/env.mjs. - Production worker watches
worker/**/*,src/server/**/*, andsrc/env.mjs.
A shared-server change can therefore release API and worker services independently from the web service. Coordinate migrations and backward-compatible contracts across those rollout windows.
The checked-in Flightcontrol database service assigns its connection string to GRAPHILE_DATABASE_URL. DATABASE_URL and DOCUMENTS_DATABASE_URL are supplied outside the visible service definition. Confirm their actual bindings in the deployment control plane. Repository configuration expresses deployment intent and does not prove the current live environment matches it.
Incident troubleshooting
Web starts and API calls fail
Check NEXT_PUBLIC_API_URL, then call both health endpoints. Confirm the API PORT, CORS allowlist, and API service logs. A green web health check gives no evidence about API health.
API or web exits during startup
Look for Invalid environment variables and the flattened field list. Compare .env or deployment secrets with src/env.mjs, including all embedding base/key slots. Also verify URL-typed fields contain complete URLs.
Jobs remain queued
Confirm the worker is running, then compare GRAPHILE_DATABASE_URL across producers and workers. Look for Worker runner connected to database. Check queue connectivity and worker logs before enqueueing duplicates.
Jobs fail once and stop
This matches the default one-attempt policy. Correct the underlying provider, database, payload, or configuration error, then trigger the product operation again. Use the recorded job ID and task identifier to correlate logs.
Context shows completed batch work with failed sources
Inspect each source status and the process-context-source logs. The source processor records FAILED without rethrowing its error, so the queue event alone can look successful. Reindex after correcting the loader, embedding, or documents-database failure.
Retrieval fails while the main product works
Check DOCUMENTS_DATABASE_URL, the vector extension, documents migrations, and embedding endpoint/key pairs. The main database can remain healthy while Context persistence or vector search is unavailable.
Streams disappear or cannot be consumed
Check Redis connectivity and API logs. Streaming state uses cache records, and a stream URL can return that no data remains when its cache entry is missing or the stream has already been consumed.
A deployment is healthy and behavior is stale
Identify which service owns the changed path and confirm that service actually released. Then check Redis and the repository's targeted cache invalidation paths. Keep process health, dependency health, deployment completion, and product behavior as separate checks.