Environment variables
Every environment variable OrbitDocs reads, for the docs site, the NestJS integration, the CLI and the self-hosted platform.
OrbitDocs keeps secrets out of orbitdocs.config.ts and out of the build. The config only names a variable (clientSecretEnv, apiKeyEnv, …). The server that serves the docs reads its value at request time.
This page lists every variable, grouped by who reads it. A name in angle brackets, such as <ai.apiKeyEnv>, means "the variable whose name you set in that config key".
Where secrets are read
Private docs and Ask AI run on whatever serves the docs. That process needs the variables in the next section:
| Docs served by | Reads variables from |
|---|---|
Your Nest app (mountOrbitDocs) | The Nest process: process.env, or the env object passed in auth.env / ai.env. |
Next server mode (proxy.ts) | The Next server's environment (on Vercel: the project's environment variables). |
| The self-hosted platform | The project's Settings → Environment only, never the platform's own environment. The session secret is derived per project. See Site environment variables. |
| A static host | Nothing. Static hosts run no OrbitDocs code. |
Private docs and Ask AI
Read by @orbitdocs/auth and @orbitdocs/ai on the serving process.
| Variable | Required | Default | What it does | Read in |
|---|---|---|---|---|
ORBITDOCS_AUTH_SECRET (name set by access.session.secretEnv) | Yes, with access | none | Signs reader sessions (HS256). At least 32 characters; generate with openssl rand -hex 32. Every instance must share it. | @orbitdocs/auth request handler, on every sign-in and gated request |
<access.providers[].clientSecretEnv> | Yes, per SSO provider | none | The OAuth client secret of that provider. | @orbitdocs/auth, when sign-in starts and on the callback |
<access.appSession.jwtSecretEnv> | No (Supabase only) | none | Shared JWT secret of a legacy Supabase project. Without it, the project's public keys (JWKS) verify the session. | @orbitdocs/auth app-session check |
<access.appSession.secretKeyEnv> | No (Clerk only) | none | Clerk secret key, to look up the reader's email when the session token has none. | @orbitdocs/auth app-session check |
<access.personalization.secretEnv> | Yes, for personalization to run | none | HMAC-SHA256 key that signs the call to personalization.url (x-orbitdocs-signature: sha256=…). When unset, the hook is skipped and sign-in goes on. | @orbitdocs/auth, after each sign-in |
<ai.apiKeyEnv> | Yes, with ai | none | API key for the LLM provider. Required for openai-compatible too; use any value if your endpoint ignores keys. | @orbitdocs/ai, on each Ask AI question |
Errors when one is missing:
| Message | Fix |
|---|---|
ORBITDOCS_AUTH_SECRET must be set to a random string of at least 32 characters (e.g. `openssl rand -hex 32`). | Set the session secret on the serving process. |
OKTA_CLIENT_SECRET is not set (client secret for Okta) | Set that provider's clientSecretEnv. |
ANTHROPIC_API_KEY is not set (API key for Ask AI) | Set ai.apiKeyEnv. |
The docs MCP server and the API MCP servers need no key. The API MCP server forwards the credentials the MCP client sends to your API.
Extraction (NestJS integration)
Read when orbitdocs extract, dev or build loads your Nest module in preview mode.
| Variable | Required | Default | What it does | Read in |
|---|---|---|---|---|
<apis[].source.nest.env> keys | No | {} | Placeholders set before the module loads, for config your app reads at import time (DATABASE_URL, JWT_SECRET, …). A variable already in the environment wins. | @orbitdocs/nestjs extractor, before require of the module |
| Your app's own variables | Depends on your app | none | Preview mode starts no providers, but code that runs on import (module-level process.env reads, ConfigModule validation) still runs. Set them, or add placeholders in env. | Your code |
PATH | No | inherited | The build command runs with <root>/node_modules/.bin first, so the project's own nest wins over a global one. | CLI, build command |
source: {
nest: {
module: 'dist/app.module.js',
build: 'nest build',
env: { DATABASE_URL: 'postgres://placeholder', STRIPE_KEY: 'sk_test_placeholder' },
},
},Docs app build and runtime
Read by @orbitdocs/next in the docs app.
| Variable | Required | Default | What it does | Read in |
|---|---|---|---|---|
NODE_ENV | No (set by Next) | development in next dev, production in builds | In development, specs are re-read on every request (the reference updates without a restart), and a Mock server environment at http://localhost:<mock.port> is added when mock is configured. | @orbitdocs/next spec loader |
ORBITDOCS_BASE_PATH | Never set it | output.basePath | Written into Next's env by withOrbitDocs so pages know the base path. Change output.basePath instead. | @orbitdocs/next plugin, at build |
The config file is TypeScript, so it can read your own variables. The sample app switches modes this way:
output: { mode: process.env.ORBITDOCS_MODE === 'server' ? 'server' : 'static', basePath: '/docs' },ORBITDOCS_MODE is the sample's own name, not something OrbitDocs reads.
CLI
| Variable | Required | Default | What it does | Read in |
|---|---|---|---|---|
ORBITDOCS_PLATFORM_URL | For publish, unless --platform is given | none | The platform URL, such as https://docs.acme.com. --platform wins. | orbitdocs publish |
ORBITDOCS_TOKEN | For publish, unless --token is given | none | A project publish token (odp_…). --token wins. | orbitdocs publish |
orbitdocs deploy --target vercel runs the Vercel CLI, which has its own login and variables. OrbitDocs reads none of them.
Generated SDK samples in the reference read the credential from an environment variable named after the operation's security scheme, such as API_KEY for a scheme named apiKey or BEARER_TOKEN for bearer (process.env.… in TypeScript, os.environ[…] in Python). See SDK samples. Those lines are sample code for your readers; OrbitDocs doesn't read them.
Platform
Read by the self-hosted platform (apps/platform). With Docker Compose, put them in apps/platform/.env: Compose reads it, and the platform container loads the whole file. See Install.
| Variable | Required | Default | What it does | Read in |
|---|---|---|---|---|
PLATFORM_SECRET | Yes | none | At least 32 characters. Signs dashboard sessions, encrypts stored Git tokens (AES-256-GCM), salts analytics visitor hashes and signs dashboard SSO state. The platform refuses to start without it. | src/config.ts |
PLATFORM_DOMAIN | No | localhost with Compose; localhost:<PORT> without | Dashboard host. Sites are at <project>.<domain>, previews at <project>--<label>.<domain>. http(s):// and a trailing / are stripped. | src/config.ts |
PLATFORM_URL | No | https://<PLATFORM_DOMAIN> with Compose; without Compose http:// for localhost and https:// otherwise | Public URL of the dashboard. Used in webhook URLs, invitation links, commit status links, SCIM locations and site URLs (its scheme). | src/config.ts |
DATABASE_URL | No | postgres://orbitdocs:orbitdocs@localhost:5433/orbitdocs; Compose sets it to the bundled Postgres | Postgres connection. Migrations run against it on every start; the build queue lives in its pgboss schema. | src/main.ts, src/config.ts |
PORT | No | 8080 | Port the platform listens on. Caddy proxies to platform:8080. | src/config.ts |
DATA_DIR | No | ./data; /data in the image | Published sites, build folders and uploads. Must be shared by all instances. | src/config.ts |
ADMIN_EMAIL | No | none | Email of the first admin. Created at start when no user has it; made an admin when one does. Needs ADMIN_PASSWORD. | src/config.ts, src/auth/auth.service.ts |
ADMIN_PASSWORD | No | none | Password for a newly created first admin. Never changes an existing account. | same |
PLATFORM_SSO | No | none (password sign-in only) | JSON array of dashboard SSO presets, the same shape as access.providers entries. Invalid JSON stops the start. | src/config.ts |
<PLATFORM_SSO[].clientSecretEnv> | Yes, per preset | none | The client secret of that SSO preset. | @orbitdocs/auth, on dashboard SSO |
SCIM_TOKEN | No | none (SCIM off) | Turns on SCIM 2.0 at /scim/v2; clients send Authorization: Bearer <token>. | src/config.ts, src/scim/scim.controller.ts |
SITE_OUTBOUND_ALLOW | No | none | Private hosts and addresses hosted sites may still reach, such as an internal Keycloak: hostnames, *.suffix, IPs or CIDRs, comma-separated. Everything private is refused otherwise. See Outbound requests. | src/config.ts, src/hosting/outbound.ts |
BUILD_CONCURRENCY | No | 1 | Git sync builds this instance runs at once. | src/git/git.service.ts |
BUILD_TIMEOUT_MS | No | 900000 (15 minutes) | Time limit for each build step (clone, install, build). A job expires after this plus 5 minutes. | src/git/git.service.ts |
NODE_ENV | No | production in the image | Set by the Dockerfile. | Node and Nest |
Hosted sites don't read this environment. Each project sets the private docs and Ask AI variables its site needs under Settings → Environment; see Site environment variables.
Docker Compose only
| Variable | Required | Default | What it does |
|---|---|---|---|
POSTGRES_PASSWORD | No | orbitdocs | Password of the bundled Postgres, used in the DATABASE_URL Compose builds. Set it before the first start: the database keeps the password it was created with. |
Your own Postgres
Set DATABASE_URL in .env to use an existing database instead of the bundled one, then remove the postgres service and the platform's depends_on from docker-compose.yml.
Given to Git sync builds
Install and build commands don't get the platform's environment. They get an allowlist of it (PATH, HOME, temp folders, locale, TLS certificates, package-manager caches and registries, proxies), the project's Build Environment Variables from Settings → Git, and these. PLATFORM_SECRET, DATABASE_URL, SSO secrets and NODE_ENV are never passed. See Git sync for the full list.
| Variable | Value | Why |
|---|---|---|
CI | true | Tools behave as in CI (no prompts, no watch mode). |
ORBITDOCS_PLATFORM_BUILD | 1 | Lets your scripts tell a platform build apart. |
GIT_TERMINAL_PROMPT | 0 | Git fails instead of waiting for a password. |

