---
title: Environment Variables
toc_max_heading_level: 3
---

This page is the source-of-truth reference for OpenReader environment variables.

:::note Recommended configuration path
Use **Settings → Admin** as the primary source of truth for shared providers and runtime site features.
`API_BASE` / `API_KEY` are optional one-time provider bootstrap seeds.
Runtime site features are seeded with `RUNTIME_SEED_JSON` / `RUNTIME_SEED_JSON_PATH`.
:::

## Quick Reference Table

| Variable | Area | Default | When to set |
| --- | --- | --- | --- |
| `LOG_FORMAT` | Runtime logging | `pretty` | Set `json` for structured logs |
| `LOG_LEVEL` | Runtime logging | `info` | Set app server log level |
| `API_BASE` | TTS provider bootstrap seed | unset | Optional first-boot base URL for `default-openai` |
| `API_KEY` | TTS provider bootstrap seed | unset | Optional first-boot API key for `default-openai` |
| `BASE_URL` | Auth | unset | Required at startup |
| `AUTH_SECRET` | Auth | unset | Required at startup |
| `AUTH_TRUSTED_ORIGINS` | Auth | empty | Add extra allowed origins |
| `USE_ANONYMOUS_AUTH_SESSIONS` | Auth | `false` | Set `true` to allow anonymous auth sessions |
| `GITHUB_CLIENT_ID` | Auth/OAuth | unset | Set with `GITHUB_CLIENT_SECRET` to enable GitHub sign-in |
| `GITHUB_CLIENT_SECRET` | Auth/OAuth | unset | Set with `GITHUB_CLIENT_ID` to enable GitHub sign-in |
| `ADMIN_EMAILS` | Admin | empty | Comma-separated emails auto-promoted to admin |
| `POSTGRES_URL` | Database | unset (SQLite mode) | Set to switch metadata/auth DB to Postgres |
| `USE_EMBEDDED_WEED_MINI` | Storage | `true` when unset | Set `false` to use external S3-compatible storage only |
| `WEED_MINI_DIR` | Storage | `docstore/seaweedfs` | Override embedded SeaweedFS data directory |
| `WEED_MINI_WAIT_SEC` | Storage | `20` | Tune SeaweedFS startup wait timeout |
| `S3_ACCESS_KEY_ID` | Storage | auto-generated in embedded mode | Set explicitly for stable/external credentials |
| `S3_SECRET_ACCESS_KEY` | Storage | auto-generated in embedded mode | Set explicitly for stable/external credentials |
| `S3_BUCKET` | Storage | `openreader-documents` in embedded mode | Required for external S3-compatible storage |
| `S3_REGION` | Storage | `us-east-1` in embedded mode | Required for external S3-compatible storage |
| `S3_ENDPOINT` | Storage | derived in embedded mode | Set for S3-compatible providers (MinIO/SeaweedFS/R2/etc.) |
| `S3_FORCE_PATH_STYLE` | Storage | `true` in embedded mode | Set per provider requirement |
| `S3_PREFIX` | Storage | `openreader` | Customize object key prefix |
| `IMPORT_LIBRARY_DIR` | Library import | `docstore/library` fallback | Set a single server library root |
| `IMPORT_LIBRARY_DIRS` | Library import | unset | Set multiple roots (comma/colon/semicolon separated) |
| `EMBEDDED_COMPUTE_WORKER_PORT` | Compute | `8081` | Override embedded worker bind port |
| `EMBEDDED_NATS_PORT` | Compute | `4222` | Override embedded NATS client port |
| `EMBEDDED_NATS_MONITOR_PORT` | Compute | `8222` | Override embedded NATS monitor port |
| `EMBEDDED_NATS_STORE_DIR` | Compute | `docstore/nats/jetstream` | Override embedded JetStream storage directory |
| `NATS_URL` | Compute | `nats://127.0.0.1:4222` in embedded startup | Override embedded startup or set standalone worker URL |
| `COMPUTE_LOG_LEVEL` | Compute | `info` | Compute worker log level |
| `COMPUTE_JOB_CONCURRENCY` | Compute | `1` | Shared compute concurrency cap |
| `COMPUTE_WHISPER_TIMEOUT_MS` | Compute | `30000` | Whisper alignment timeout budget |
| `COMPUTE_PDF_TIMEOUT_MS` | Compute | `300000` | PDF parse timeout budget |
| `COMPUTE_OP_STALE_MS` | Compute | `max(30m, 4x max compute timeout)` | Shared stale window for compute op replacement |
| `WHISPER_MODEL_BASE_URL` | Compute model source | onnx-community default | Override Whisper ONNX model base URL |
| `PDF_LAYOUT_MODEL_BASE_URL` | Compute model source | PP-DocLayoutV3 default | Override PDF layout ONNX model base URL |
| `COMPUTE_WORKER_URL` | External compute mode | unset | Set only for standalone external worker mode |
| `COMPUTE_WORKER_TOKEN` | External compute mode | unset | Required for standalone external worker auth |
| `FFMPEG_BIN` | Audio runtime | auto-detected (`ffmpeg-static`) | Override ffmpeg binary path |
| `DISABLE_AUTH_RATE_LIMIT` | Auth request throttling | `false` | Set `true` to disable Better Auth request rate limiting |
| `ENABLE_TEST_NAMESPACE` | Testing/CI | unset | Honor `x-openreader-test-namespace` header in production builds |
| `RUN_DRIZZLE_MIGRATIONS` | DB migrations | `true` | Set `false` to skip startup Drizzle migrations |
| `RUN_FS_MIGRATIONS` | Storage migrations | `true` | Set `false` to skip startup filesystem -> S3/DB migration pass |
| `RUNTIME_SEED_JSON_PATH` | Runtime JSON seed | unset | Absolute path to first-boot JSON seed document |
| `RUNTIME_SEED_JSON` | Runtime JSON seed | unset | Inline first-boot JSON seed document |

## Runtime Logging

### LOG_FORMAT

Controls log output format for server-side Pino loggers.

- Default: `pretty`
- Allowed values: `pretty`, `json`
- Applies to app server and compute worker

### LOG_LEVEL

App server log level.

- Default: `info`

## TTS Provider and Request Behavior

### API_BASE

Optional first-boot bootstrap base URL for the auto-created `default-openai` shared provider.

- Example: `http://host.docker.internal:8880/v1`
- Read only for provider bootstrap when shared providers are empty and `API_KEY` is set.
- After bootstrap, provider configuration is DB-backed and managed in **Settings → Admin → Shared providers**.

### API_KEY

Optional first-boot bootstrap API key for the auto-created `default-openai` shared provider.

- Read only for provider bootstrap when shared providers are empty.
- Stored encrypted at rest after bootstrap.
- After bootstrap, provider configuration is DB-backed and managed in **Settings → Admin → Shared providers**.

### TTS Daily Rate Limiting (Runtime Settings)

Managed as runtime config in **Settings → Admin → Site features**.

- `disableTtsRateLimit` default: `true` (daily TTS limits disabled)
- `ttsDailyLimitAnonymous` default: `50000`
- `ttsDailyLimitAuthenticated` default: `500000`
- `ttsIpDailyLimitAnonymous` default: `100000`
- `ttsIpDailyLimitAuthenticated` default: `1000000`

### TTS Upstream Settings (Runtime Settings)

Managed as runtime config in **Settings → Admin → Site features → TTS upstream**.

- `ttsUpstreamMaxRetries` default: `2`
- `ttsUpstreamTimeoutMs` default: `285000`
- `ttsCacheMaxSizeBytes` default: `268435456` (256 MB)
- `ttsCacheTtlMs` default: `1800000` (30 minutes)

There are no dedicated env vars for these runtime settings.

## Auth and Identity

### BASE_URL

Required external base URL for this OpenReader instance.

- Required at startup
- Example: `http://localhost:3003` or `https://reader.example.com`

### AUTH_SECRET

Required secret key used by auth/session handling.

- Required at startup
- Generate with `openssl rand -hex 32`

### AUTH_TRUSTED_ORIGINS

Additional allowed origins for auth requests.

- Comma-separated list
- `BASE_URL` origin is trusted automatically

### USE_ANONYMOUS_AUTH_SESSIONS

Controls whether auth-enabled deployments can create/use anonymous sessions.

- Default: `false`

### GITHUB_CLIENT_ID

GitHub OAuth client ID.

- Set with `GITHUB_CLIENT_SECRET`

### GITHUB_CLIENT_SECRET

GitHub OAuth client secret.

- Set with `GITHUB_CLIENT_ID`

### ADMIN_EMAILS

Comma-separated list of email addresses auto-promoted to admin.

- Requires auth to be enabled
- Admins can manage shared providers and runtime site features in-app

## Database and Object Blob Storage

### POSTGRES_URL

Switches metadata/auth storage from SQLite to Postgres.

- Unset: SQLite at `docstore/sqlite3.db`
- Set: Postgres mode

### USE_EMBEDDED_WEED_MINI

Controls embedded SeaweedFS startup.

- Default behavior: treated as enabled when unset
- Set `false` to rely on external S3-compatible storage

### WEED_MINI_DIR

Data directory for embedded SeaweedFS (`weed mini`).

- Default: `docstore/seaweedfs`

### WEED_MINI_WAIT_SEC

Max wait time for embedded SeaweedFS startup.

- Default: `20`

### S3_ACCESS_KEY_ID

S3 access key.

- Optional in embedded mode (auto-generated when unset)
- Required for external S3 mode

### S3_SECRET_ACCESS_KEY

S3 secret key.

- Optional in embedded mode (auto-generated when unset)
- Required for external S3 mode

### S3_BUCKET

S3 bucket name.

- Embedded default: `openreader-documents`
- Required for external S3 mode

### S3_REGION

S3 region.

- Embedded default: `us-east-1`
- Required for external S3 mode

### S3_ENDPOINT

Custom endpoint for S3-compatible providers.

- Optional for AWS
- Typical for MinIO/SeaweedFS/R2

### S3_FORCE_PATH_STYLE

Force path-style S3 URLs.

- Embedded default: `true`

### S3_PREFIX

Object key prefix.

- Default: `openreader`

## Library Import

### IMPORT_LIBRARY_DIR

Single library source directory.

### IMPORT_LIBRARY_DIRS

Multiple library roots.

- Supports comma, colon, or semicolon-separated values

## Compute Worker and Model Configuration

### EMBEDDED_COMPUTE_WORKER_PORT

Embedded compute worker port.

- Default: `8081`

### EMBEDDED_NATS_PORT

Embedded NATS client port.

- Default: `4222`

### EMBEDDED_NATS_MONITOR_PORT

Embedded NATS monitor port.

- Default: `8222`

### EMBEDDED_NATS_STORE_DIR

Embedded NATS JetStream data directory.

- Default: `docstore/nats/jetstream`

### NATS_URL

NATS URL used by compute services.

- Embedded startup default: `nats://127.0.0.1:4222`

### COMPUTE_LOG_LEVEL

Compute worker log level.

- Default: `info`

### COMPUTE_JOB_CONCURRENCY

Max concurrent compute jobs per worker.

- Default: `1`

### COMPUTE_WHISPER_TIMEOUT_MS

Whisper alignment timeout budget.

- Default: `30000`

### COMPUTE_PDF_TIMEOUT_MS

PDF parse timeout budget.

- Default: `300000`

### COMPUTE_OP_STALE_MS

Stale operation window before worker/app cleanup logic can replace an op.

- Default: `max(30m, 4x max compute timeout)`

### WHISPER_MODEL_BASE_URL

Base URL for Whisper ONNX model downloads.

### PDF_LAYOUT_MODEL_BASE_URL

Base URL for PDF layout model downloads.

### COMPUTE_WORKER_URL

External compute worker URL.

- Leave unset for embedded worker mode

### COMPUTE_WORKER_TOKEN

Shared token for app-to-external-worker requests.

## Compute PDF Parsing Rate Limiting (Runtime Settings)

Managed as runtime config in **Settings → Admin → Site features**.

- `disableComputeRateLimit` default: `true`
- `computeParseBurstMax` default: `8`
- `computeParseBurstWindowSec` default: `60`
- `computeParseSustainedMax` default: `24`
- `computeParseSustainedWindowSec` default: `600`
- `maxUploadMb` default: `200`

There are no dedicated env vars for these runtime settings.

## Audio Runtime

### FFMPEG_BIN

Override ffmpeg binary path used for audio processing.

- Used by audiobook processing routes and compute worker Whisper audio decode.

## Testing and CI

### DISABLE_AUTH_RATE_LIMIT

Disables Better Auth request rate limiting.

- Default: `false`

### ENABLE_TEST_NAMESPACE

Enables the `x-openreader-test-namespace` header path in production builds.

## Migration Controls

### RUN_DRIZZLE_MIGRATIONS

Controls startup Drizzle schema migrations.

- Default: `true`
- Set `false` to skip startup migration run

### RUN_FS_MIGRATIONS

Controls startup filesystem-to-S3/DB migration pass.

- Default: `true`
- Set `false` to skip startup storage migration run

## Runtime JSON Seed (v4)

### RUNTIME_SEED_JSON_PATH

Path-based first-boot seed document.

- If both `RUNTIME_SEED_JSON_PATH` and `RUNTIME_SEED_JSON` are set, path wins.
- Value must point to a JSON file readable by the app process.

### RUNTIME_SEED_JSON

Inline first-boot seed document.

- Used only when `RUNTIME_SEED_JSON_PATH` is unset.
- Must be a JSON object with `version: 1`.

Supported top-level keys:

- `version` (required, must be `1`)
- `runtimeConfig` (optional object, strict-validated against runtime schema)
- `providers` (optional array of shared provider seed entries)

Example:

```json
{
  "version": 1,
  "runtimeConfig": {
    "enableUserSignups": true,
    "restrictUserApiKeys": true,
    "defaultTtsProvider": "custom-openai",
    "enableTtsProvidersTab": true,
    "enableAudiobookExport": true,
    "enableDocxConversion": true,
    "showAllProviderModels": true,
    "disableTtsRateLimit": true,
    "ttsDailyLimitAnonymous": 50000,
    "ttsDailyLimitAuthenticated": 500000,
    "ttsIpDailyLimitAnonymous": 100000,
    "ttsIpDailyLimitAuthenticated": 1000000,
    "ttsCacheMaxSizeBytes": 268435456,
    "ttsCacheTtlMs": 1800000,
    "ttsUpstreamMaxRetries": 2,
    "ttsUpstreamTimeoutMs": 285000,
    "disableComputeRateLimit": true,
    "computeParseBurstMax": 8,
    "computeParseBurstWindowSec": 60,
    "computeParseSustainedMax": 24,
    "computeParseSustainedWindowSec": 600,
    "maxUploadMb": 200,
    "changelogFeedUrl": "https://docs.openreader.richardr.dev/changelog/manifest.json"
  },
  "providers": [
    {
      "slug": "default-openai",
      "displayName": "Default (seeded)",
      "providerType": "custom-openai",
      "baseUrl": "http://localhost:8880/v1",
      "apiKey": "api_key_optional",
      "defaultModel": "kokoro",
      "enabled": true
    }
  ]
}
```

Provider fallback behavior:

- If the JSON seed includes `providers` (including an empty array), `API_BASE` / `API_KEY` fallback is skipped.
- If the JSON seed does not include a `providers` key, the legacy `API_BASE` / `API_KEY` bootstrap fallback can still create `default-openai` when provider rows are empty.

Precedence summary:

- Runtime reads: admin DB runtime rows override built-in defaults.
- Seed input (`RUNTIME_SEED_JSON*`) only populates missing runtime rows on first boot; it does not overwrite existing/admin-edited rows.
- Provider bootstrap order: JSON `providers` section > `API_BASE`/`API_KEY` fallback > no provider bootstrap.

## Related

- [Admin Panel](../configure/admin-panel)
- [TTS Providers](../configure/tts-providers)
- [Local Development](../deploy/local-development)
- [Vercel Deployment](../deploy/vercel-deployment)
