Skip to main content
Every variable here is described once, in packages/env in the application repository, and read from there by three things: the check each app runs at boot, the type of the ragen.config.ts the installer writes, and this page. They cannot drift from each other, which is the point — an earlier hand-written version of this reference disagreed with the code about which S3 variables were required. A required variable is required once you have chosen that provider, not in general. Everything optional has a working default in code.

Providers

One variable picks an implementation, and that choice decides what else you have to set.

Storage

Chosen with STORAGE_PROVIDER, which defaults to local.

STORAGE_PROVIDER=local

Files on the container filesystem. STORAGE_LOCAL_PATH defaults to ./data/storage, so nothing is mandatory — but every process that touches files needs the same volume.

STORAGE_PROVIDER=s3

Any S3-compatible store. S3_ENDPOINT_URL is optional — unset selects the default AWS endpoint, and it is set to point at R2, Scaleway, MinIO or Ceph. S3_SESSION_TOKEN is for temporary credentials; S3_FORCE_PATH_STYLE for stores that need path-style addressing.

Encryption

Chosen with ENCRYPTION_PROVIDER. Unset auto-detects from whichever credentials are present, in the order Scaleway, KMS, local (getKeyProvider()). A deployed environment with no provider at all refuses to start, unless ALLOW_UNENCRYPTED=1 says so deliberately.

ENCRYPTION_PROVIDER=scaleway

Scaleway Key Manager (ADR-02).

ENCRYPTION_PROVIDER=kms

AWS KMS.

ENCRYPTION_PROVIDER=local

A key in the environment. Present is not the same as usable: the value is parsed by @ragenai/crypto, not here.

Reranker

Chosen with RERANK_PROVIDER, which defaults to scaleway.

RERANK_PROVIDER=scaleway

Scaleway /v1/rerank (qwen3-embedding-8b). The default. SCW_API_KEY is the same account key the Scaleway encryption provider uses — one key, two features.

RERANK_PROVIDER=cohere

Cohere Rerank v3.5, over any endpoint speaking Cohere’s /rerank shape. RERANK_COHERE_BASE_URL names it, and there is nothing to fall back to: the LITELLM_PROXY_URL default went with the proxy in B6, so selecting this variant without an endpoint reranks nothing. That failure is silent by design — an unreachable reranker degrades to no reranking rather than erroring — so set the URL deliberately and confirm reranking is happening rather than assuming it.

Mail

Chosen with MAIL_PROVIDER. Unset detects from credentials: RESEND_API_KEY selects Resend, SMTP_HOST selects SMTP. With neither, outside production the message is logged instead of sent, and in production getMailProvider() throws rather than let an operator silently lose every invitation.

MAIL_PROVIDER=resend

Resend.

MAIL_PROVIDER=smtp

Any SMTP relay. Only the host is required: SMTP_PORT defaults to 587, and authentication is set only when SMTP_USER is given, because unauthenticated relays are real.

MAIL_PROVIDER=console

Log the message instead of sending it. What a laptop wants, and an explicit way to say in production that no email will be delivered — which the mailer otherwise refuses to assume. Needs nothing else.

Speech

Chosen with SPEECH_PROVIDER. Unset leaves speech off, unless ELEVENLABS_API_KEY is set, which selects ElevenLabs. Deliberately not detected from OPENAI_API_KEY: that key is there for chat, and speech bills per request.

SPEECH_PROVIDER=elevenlabs

ElevenLabs, for both synthesis and transcription.

SPEECH_PROVIDER=openai

OpenAI’s /v1/audio/* API. Nothing is required here because SPEECH_API_KEY falls back to OPENAI_API_KEY, which a deployment reaching OpenAI already has; SPEECH_BASE_URL defaults to OpenAI itself and is how you point at vLLM or a proxy instead.

Settings

No choice to make — these are the same variables whatever else is configured.

Database

Postgres. directUrl bypasses a connection pooler for migrations; without it the pooled URL is used for both.

Model gateway

Ragen calls model providers itself through @ragenai/llm-gateway (ADR-49). A route table names the upstream for each model id and credentials come per provider, so those credentials have to be present in the web, api and worker processes. The table is infra/llm-gateway/routes.yaml; LLM_ROUTES_PATH points at your own instead. There is no proxy path and no flag choosing between the two: B6 removed both, and an external gateway — Portkey, vLLM, Ollama, or a LiteLLM you run yourself — attaches as an openai-compatible route rather than as a mode.

Vector store

Qdrant, the only supported vector store (ADR-31). The URL falls back to http://localhost:6333 in code, which is why it is optional here and required outright in a deployed apps/worker — a silent fallback there once wrote every vector into a container-local Qdrant and reported success.

Models

Defaults for each job. All optional: each has a fallback in code, and apps/worker requires embeddings and vectorSize outright because its ingest cannot guess either. Changing embeddings or vectorSize after documents exist invalidates the collection.

Temporal

Document ingest runs as Temporal workflows (ADR-26). Optional here because apps/web and apps/api fall back to localhost:7233, and required outright in apps/worker, which is the process that runs them. TEMPORAL_CERT and TEMPORAL_KEY are deliberately absent: they are declared in the schema but nothing reads them yet.

Redis

Required by apps/worker, which caches organization settings through it. Genuinely optional in apps/web, where the absence is a real mode rather than a degraded one — the settings cache computes values directly, and the public chatbot rate limiter fails open, so rate limiting is off rather than enforced with a fallback limit.

Observability

OpenTelemetry (ADR-22). A no-op in apps/web without an endpoint; apps/worker also traces on a Langfuse key alone.

Token vault

Connector OAuth tokens and API keys (ADR-32). Each URL and its secret are all-or-nothing: a URL without its secret produces 401s rather than a legible error. The token vault is all-or-nothing: RAGEN_TOKEN_VAULT_URL and RAGEN_TOKEN_VAULT_SERVICE_SECRET must both be set or both omitted. The vault is all-or-nothing: RAGEN_VAULT_URL and RAGEN_VAULT_SERVICE_SECRET must both be set or both omitted.