Skip to main content
Version: v5.3.0

Environment Variables

Environment variables cover infrastructure: addresses, secrets, database, storage and the compute worker. Everything else, from providers to sign-up policy, is set in Settings → Admin and takes effect without a restart.

All variables are server-side; none reach the browser. Startup checks the configuration and lists every problem at once.

Minimal configuration​

A single container or pnpm start needs only two values:

BASE_URL=http://localhost:3003 # the address you open in the browser
AUTH_SECRET=<openssl rand -base64 32> # keep it stable; it also encrypts saved keys

Off localhost, also set COMPUTE_WORKER_PUBLIC_URL to the address browsers use for port 8081. With the embedded worker, the worker and broker tokens are generated and the playback secret is derived from AUTH_SECRET, so you set none of them.

Upgrading a v4 instance? Follow Upgrade from v4.

Sign-in​

VariableDefaultPurpose
BASE_URLrequiredThe address people open, for example https://reader.example.com.
AUTH_SECRETrequiredSigns sessions and encrypts saved keys. App only; never set it on a standalone worker. Keep it stable.
AUTH_TRUSTED_ORIGINSemptyOther addresses allowed to sign in, comma-separated. BASE_URL is always trusted.
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRETunsetTurn on GitHub sign-in.
OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_DISCOVERY_URLunsetTurn on OpenID Connect sign-in. The discovery URL must be https:// except on localhost.
OIDC_PROVIDER_IDoidcUsed in the callback path <BASE_URL>/api/auth/callback/<id>.
OIDC_PROVIDER_NAMESSOThe sign-in button label.
OIDC_SCOPESopenid profile emailRequested scopes, space- or comma-separated.
BOOTSTRAP_ADMIN_EMAILunsetThe first administrator on a new database.
BOOTSTRAP_ADMIN_PASSWORDunsetTheir initial password, at least 16 characters.
BOOTSTRAP_ADMIN_PASSWORD_FILEunsetRead the initial password from a file instead (one trailing newline is removed).
CRON_SECRETunsetRequired on Vercel; authorizes the scheduled-task cron.
DISABLE_AUTH_RATE_LIMITfalseTurns off sign-in rate limiting. For tests only.

Setup details are in Users and Sign-in. ADMIN_EMAILS from v4 is no longer used.

DESKTOP_LAUNCH_TOKEN​

Internal to the desktop app; never set it yourself. The desktop shell generates a new token on every launch and exchanges it once at POST /api/auth/desktop/session for a session for the app's single owner, who has no password. When it is unset, that route does not exist. It also switches the interface to the desktop layout. Startup refuses a value shorter than 32 characters, and the embedded worker, NATS and SeaweedFS never receive it.

RICHARDRDEV_PRODUCTION​

Only for the official instance at openreader.richardr.dev. Exactly true shows the official-instance label and privacy notice and limits requests to the US. Leave it unset.

Database and Object Blob Storage​

VariableDefaultPurpose
POSTGRES_URLunsetUse Postgres instead of SQLite.
SQLITE_DB_PATHdocstore/sqlite3.dbMove the SQLite file. Relative paths start from the app directory.
USE_EMBEDDED_WEED_MINItrueSet false to use external S3-compatible storage.
S3_BUCKETopenreader-documents (embedded)Required for external storage.
S3_REGIONus-east-1 (embedded)Required for external storage.
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYgenerated (embedded)Required for external storage.
S3_INTERNAL_ENDPOINThttp://127.0.0.1:8333 (embedded)The endpoint the app and worker use. Required for external storage, AWS included (https://s3.<region>.amazonaws.com). A separate worker needs an address it can reach, which may differ from the app's.
S3_PUBLIC_ENDPOINTunsetA public HTTPS endpoint for browsers, used with presigned transfers.
S3_BROWSER_TRANSPORTautoproxy, presigned or auto. See how browsers reach files. proxy is refused on Vercel.
S3_FORCE_PATH_STYLEtrue (embedded)Path-style URLs, as many non-AWS services require.
S3_PREFIXopenreaderPrefix for every object key.
S3_AUTO_CREATE_BUCKETfalseCreate the bucket on startup if it is missing. The full Compose examples turn it on.
WEED_MINI_DIRdocstore/seaweedfsEmbedded SeaweedFS data directory.
WEED_MINI_PORT8333Embedded SeaweedFS S3 port. If a port is taken, startup moves SeaweedFS's internal ports.
WEED_MINI_BIND_HOST127.0.0.1Embedded SeaweedFS listening address.
WEED_MINI_ADVERTISE_HOSTthe bind hostThe address embedded SeaweedFS advertises. When bound to all interfaces, a private address is detected.
WEED_MINI_WAIT_SEC20How long startup waits for embedded SeaweedFS.

Library Import​

VariableDefaultPurpose
IMPORT_LIBRARY_DIRSdocstore/libraryFolders for Server Library Import, separated by commas or semicolons, and also colons except on Windows.

Compute Worker and Model Configuration​

Connecting the app and worker​

Only needed with a standalone worker.

VariableSet onPurpose
COMPUTE_WORKER_URLappThe worker's address. Leave unset to run the embedded worker.
COMPUTE_WORKER_PUBLIC_URLappThe worker address browsers load audio from. Defaults to COMPUTE_WORKER_URL. Also needed with the embedded worker when the app is not on localhost.
COMPUTE_WORKER_TOKENbothAuthorizes the app's requests to the worker.
COMPUTE_CREDENTIAL_BROKER_TOKENbothAuthorizes the worker's requests to the app for provider keys.
COMPUTE_CREDENTIAL_BROKER_URLworkerhttps://<app>/api/internal/compute/tts-credentials. Never the worker's public address.
COMPUTE_CREDENTIAL_BROKER_TIMEOUT_MSworkerDefault 5000.
TTS_PLAYBACK_TOKEN_SECRETbothSigns short-lived audio links and keys the audio cache. See below.
NATS_URLworkerThe NATS server. The embedded worker uses nats://127.0.0.1:4222.
NATS_CREDS or NATS_CREDS_FILEworkerNATS credentials, inline or as a file path. NATS_CREDS wins when both are set.

Generate each token with openssl rand -base64 32 and use the same value on both sides.

TTS_PLAYBACK_TOKEN_SECRET is optional with the embedded worker: it is derived from AUTH_SECRET, and a value you set always wins. A standalone worker never receives AUTH_SECRET, so it needs the explicit value. Changing it, or AUTH_SECRET when derived, makes cached audio regenerate. The example Compose files ship a local default; replace it before exposing a stack beyond your machine.

Worker tuning​

These have sensible defaults. With the embedded worker, set them in the app's .env.

VariableDefaultPurpose
PORT8081Standalone worker port. Platforms usually set it. (The app image uses 3003 for itself.)
COMPUTE_WORKER_HOST127.0.0.1 embedded, 0.0.0.0 standaloneListening address. The Docker image sets 0.0.0.0 so port 8081 can be published.
COMPUTE_LOG_LEVELinfoWorker log level.
COMPUTE_WHISPER_TIMEOUT_MS30000Word-timing alignment timeout.
COMPUTE_TTS_PLAYBACK_SEGMENT_TIMEOUT_MSthe Whisper timeoutTimeout for generating one segment.
COMPUTE_PDF_TIMEOUT_MS300000PDF layout analysis timeout.
COMPUTE_PDF_JOB_ATTEMPTS1Attempts per PDF layout job.
COMPUTE_OP_STALE_MS30 minutes, or 4× the longest timeoutWhen an unfinished operation may be replaced.
COMPUTE_PREWARM_MODELSfalseDownload models at startup instead of on first use.
WHISPER_MODEL_BASE_URLHugging FaceMirror for Whisper model files.
PDF_LAYOUT_MODEL_BASE_URLHugging FaceMirror for PDF layout model files.
COMPUTE_JOBS_STREAM_MAX_BYTES268435456JetStream jobs stream size.
COMPUTE_EVENTS_STREAM_MAX_BYTES134217728JetStream events stream size.
COMPUTE_JOB_STATES_MAX_BYTES67108864JetStream job-state store size.
COMPUTE_NATS_REPLICAS1JetStream replicas: 1, 3 or 5 for a NATS cluster. Other values become 1.
FFMPEG_BINsee belowThe ffmpeg used for audio conversion, timing and audiobook export.

FFMPEG_BIN is set to a pinned LGPL build in the published images and the desktop app, and to the ffmpeg-static dependency by pnpm dev/pnpm start. Otherwise ffmpeg is taken from PATH.

Embedded services​

VariableDefaultPurpose
EMBEDDED_COMPUTE_WORKER_PORT8081Embedded worker port.
EMBEDDED_NATS_PORT4222Embedded NATS client port.
EMBEDDED_NATS_MONITOR_PORT8222Embedded NATS monitoring port.
EMBEDDED_NATS_STORE_DIRdocstore/nats/jetstreamEmbedded JetStream data directory.

Logging​

VariableDefaultPurpose
LOG_FORMATprettyjson for structured logs, on the app and worker.
LOG_LEVELinfoApp log level. The worker uses COMPUTE_LOG_LEVEL.

Migration Controls​

VariableDefaultPurpose
RUN_DRIZZLE_MIGRATIONStrueSet false to skip database migrations on startup.
RUN_V4_DECOMMISSIONtrueSet false to skip deleting retired v4 storage on startup.

See Database and Migrations.

TTS Provider and Request Behavior​

VariableDefaultPurpose
API_BASEunsetOn first start, creates a default-openai provider with this base URL.
API_KEYunsetThat provider's API key. API_BASE alone is enough for servers without keys.
API_MODEL_NAMEkokoroThat provider's default model. Does not create a provider by itself.

These are read only while no providers exist and the seed below has no providers key. See Seed a provider from the environment.

Runtime JSON Seed​

RUNTIME_SEED_JSON (inline) or RUNTIME_SEED_JSON_PATH (a file; wins when both are set) fills in admin settings on first start. Seeds only add values that are missing. They never overwrite a value an admin has set, and changing the seed later does nothing. See Settings from seeds.

{
"version": 1,
"runtimeConfig": {
"signupPolicy": "open",
"allowAnonymousSessions": false,
"defaultTtsProvider": "default-openai",
"enableTtsProvidersTab": true,
"enableAudiobookExport": true,
"enableDocxConversion": true,
"showAllProviderModels": true,
"ttsCacheMaxSizeBytes": 268435456,
"ttsCacheTtlMs": 1800000,
"ttsUpstreamMaxRetries": 2,
"ttsUpstreamTimeoutMs": 285000,
"ttsPlaybackBackgroundExtent": "section",
"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",
"defaultModel": "kokoro",
"enabled": true
}
],
"accountEmail": {
"enabled": true,
"senderName": "OpenReader",
"senderEmail": "mail@example.com",
"replyTo": "support@example.com",
"apiKey": "REPLACE_WITH_RESEND_API_KEY"
},
"gutendex": {
"enabled": true,
"serverUrl": "https://gutendex.example.com",
"apiKey": "REPLACE_WITH_GUTENDEX_API_KEY"
}
}
KeyNotes
versionRequired; must be 1.
runtimeConfigAny instance or compute setting, by key. An invalid seed, such as one with unknown keys, is logged and not applied; startup continues. computeLimitPolicies must be a complete policy; start from examples/openreader-seed.json.
providersShared providers. When present, even as [], the API_* variables are ignored.
accountEmailResend settings. enabled, senderName, senderEmail and apiKey are required; replyTo may be null.
gutendexA Project Gutenberg catalog server. Omit it to use the public gutendex.com. When present, serverUrl is required, enabled defaults to true, and apiKey may be null.

API keys in a seed are encrypted with AUTH_SECRET when stored. Keep the seed secret and out of version control.

Platform-Supplied Signals​

Set by Node.js, Next.js, the host or the test runner; not configuration.

VariableSet byEffect
NODE_ENVNode.js / Next.jsSecure cookies and production behavior.
NEXT_RUNTIMENext.jsLoads server-only startup code.
VERCELVercelCron-driven scheduled tasks, request IP handling, and refusing proxy storage transfers.
CICI runnersTest retries and reporters.
PWDThe shellWhere the worker looks for docstore when run from source.

Documentation Build Variables​

Read only by scripts/build-changelog-feed.mjs and the docs deployment workflow.

VariableDefaultPurpose
CHANGELOG_REPOGITHUB_REPOSITORY, then richardr1126/openreaderRepository whose releases are read.
CHANGELOG_PUBLIC_BASE_URLhttps://docs.openreader.richardr.devBase of generated changelog links.
CHANGELOG_MUTABLE_COUNT3Recent releases refreshed on each build.
CHANGELOG_FORCE_FULLunsetExactly 1 rebuilds the whole changelog.
GITHUB_TOKEN, GITHUB_REPOSITORY, GITHUB_EVENT_PATH, GITHUB_EVENT_NAMEGitHub ActionsRelease API access and event handling.