> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ragen.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration reference

> Every environment variable Ragen reads, generated from the schemas the applications validate against at boot.

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.

| Variable             | Config field |          |
| -------------------- | ------------ | -------- |
| `STORAGE_LOCAL_PATH` | `path`       | optional |

#### `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.

| Variable               | Config field      |              |
| ---------------------- | ----------------- | ------------ |
| `S3_BUCKET_NAME`       | `bucketName`      | **required** |
| `S3_REGION`            | `region`          | **required** |
| `S3_ACCESS_KEY_ID`     | `accessKeyId`     | **required** |
| `S3_SECRET_ACCESS_KEY` | `secretAccessKey` | **required** |
| `S3_ENDPOINT_URL`      | `endpoint`        | optional     |
| `S3_SESSION_TOKEN`     | `sessionToken`    | optional     |
| `S3_FORCE_PATH_STYLE`  | `forcePathStyle`  | optional     |

### 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](https://github.com/webamigos/RagenAI/blob/main/docs/adrs/02-per-org-kms-keys.md)).

| Variable                 | Config field |              |
| ------------------------ | ------------ | ------------ |
| `SCW_KEY_MANAGER_KEY_ID` | `keyId`      | **required** |
| `SCW_API_KEY`            | `apiKey`     | **required** |
| `SCW_KEY_MANAGER_REGION` | `region`     | optional     |

#### `ENCRYPTION_PROVIDER=kms`

AWS KMS.

| Variable             | Config field |              |
| -------------------- | ------------ | ------------ |
| `AWS_KMS_KEY_ID`     | `keyId`      | **required** |
| `AWS_DEFAULT_REGION` | `region`     | optional     |
| `AWS_ENDPOINT_URL`   | `endpoint`   | optional     |

#### `ENCRYPTION_PROVIDER=local`

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

| Variable                | Config field |              |
| ----------------------- | ------------ | ------------ |
| `ENCRYPTION_MASTER_KEY` | `masterKey`  | **required** |

### 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.

| Variable       | Config field |              |
| -------------- | ------------ | ------------ |
| `SCW_API_BASE` | `apiBase`    | **required** |
| `SCW_API_KEY`  | `apiKey`     | **required** |
| `RERANK_MODEL` | `model`      | optional     |

#### `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.

| Variable                 | Config field |              |
| ------------------------ | ------------ | ------------ |
| `RERANK_COHERE_BASE_URL` | `baseUrl`    | **required** |
| `RERANK_COHERE_API_KEY`  | `apiKey`     | optional     |
| `RERANK_MODEL`           | `model`      | optional     |

### 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.

| Variable                    | Config field       |              |
| --------------------------- | ------------------ | ------------ |
| `RESEND_API_KEY`            | `apiKey`           | **required** |
| `RESEND_DEFAULT_SEGMENT_ID` | `defaultSegmentId` | optional     |

#### `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.

| Variable      | Config field |              |
| ------------- | ------------ | ------------ |
| `SMTP_HOST`   | `host`       | **required** |
| `SMTP_PORT`   | `port`       | optional     |
| `SMTP_USER`   | `user`       | optional     |
| `SMTP_PASS`   | `password`   | optional     |
| `SMTP_SECURE` | `secure`     | optional     |

#### `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.

| Variable             | Config field |              |
| -------------------- | ------------ | ------------ |
| `ELEVENLABS_API_KEY` | `apiKey`     | **required** |

#### `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.

| Variable          | Config field |          |
| ----------------- | ------------ | -------- |
| `SPEECH_BASE_URL` | `baseUrl`    | optional |
| `SPEECH_API_KEY`  | `apiKey`     | optional |
| `TTS_MODEL`       | `ttsModel`   | optional |
| `STT_MODEL`       | `sttModel`   | optional |

## 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.

| Variable              | Config field |              |
| --------------------- | ------------ | ------------ |
| `DATABASE_URL`        | `url`        | **required** |
| `DATABASE_DIRECT_URL` | `directUrl`  | optional     |

### Model gateway

Ragen calls model providers itself through `@ragenai/llm-gateway` ([ADR-49](https://github.com/webamigos/RagenAI/blob/main/docs/adrs/49-the-application-calls-model-providers-itself.md)). 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.

| Variable          | Config field |          |
| ----------------- | ------------ | -------- |
| `LLM_ROUTES_PATH` | `routesPath` | optional |

### Vector store

Qdrant, the only supported vector store ([ADR-31](https://github.com/webamigos/RagenAI/blob/main/docs/adrs/31-only-qdrant-is-a-supported-vector-store.md)). The URL falls back to [http://localhost:6333](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.

| Variable         | Config field |          |
| ---------------- | ------------ | -------- |
| `QDRANT_URL`     | `url`        | optional |
| `QDRANT_API_KEY` | `apiKey`     | optional |

### 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.

| Variable                 | Config field          |          |
| ------------------------ | --------------------- | -------- |
| `DEFAULT_MODEL`          | `chat`                | optional |
| `DEFAULT_MODEL_PROVIDER` | `chatProvider`        | optional |
| `REPHRASE_MODEL`         | `rephrase`            | optional |
| `REPHRASE_TEMPERATURE`   | `rephraseTemperature` | optional |
| `SUMMARY_MODEL`          | `summary`             | optional |
| `EMBEDDINGS_MODEL`       | `embeddings`          | optional |
| `VECTOR_SIZE`            | `vectorSize`          | optional |

### Temporal

Document ingest runs as Temporal workflows ([ADR-26](https://github.com/webamigos/RagenAI/blob/main/docs/adrs/26-absorb-ragen-worker-into-monorepo.md)). 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.

| Variable                  | Config field |          |
| ------------------------- | ------------ | -------- |
| `TEMPORAL_SERVER_ADDRESS` | `address`    | optional |
| `TEMPORAL_NAMESPACE`      | `namespace`  | optional |

### 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.

| Variable    | Config field |          |
| ----------- | ------------ | -------- |
| `REDIS_URL` | `url`        | optional |

### Observability

OpenTelemetry ([ADR-22](https://github.com/webamigos/RagenAI/blob/main/docs/adrs/22-observability-opentelemetry.md)). A no-op in apps/web without an endpoint; apps/worker also traces on a Langfuse key alone.

| Variable                      | Config field  |          |
| ----------------------------- | ------------- | -------- |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `endpoint`    | optional |
| `OTEL_SERVICE_NAME`           | `serviceName` | optional |

### Token vault

Connector OAuth tokens and API keys ([ADR-32](https://github.com/webamigos/RagenAI/blob/main/docs/adrs/32-token-vault-and-mcp-stay-separate.md)). Each URL and its secret are all-or-nothing: a URL without its secret produces 401s rather than a legible error.

| Variable                           | Config field      |          |
| ---------------------------------- | ----------------- | -------- |
| `RAGEN_TOKEN_VAULT_URL`            | `connectorUrl`    | optional |
| `RAGEN_TOKEN_VAULT_SERVICE_SECRET` | `connectorSecret` | optional |
| `RAGEN_VAULT_URL`                  | `url`             | optional |
| `RAGEN_VAULT_SERVICE_SECRET`       | `secret`          | optional |

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.
