OrbitDocs packages are coming to npm soon. Until then, run it from the GitHub repo →
Deploy

Server mode

Run the docs as a Next server so private docs, Ask AI, MCP and real redirects work on Vercel, a container or any Node host.

In server mode the docs app is a running Next server instead of a folder of files. A proxy.ts file sees every request first, so it can sign readers in, gate pages and answer Ask AI. Use it when you need those features and don't serve the docs from your Nest app.

When to use server mode

You needStaticServer mode
Private docs (access)Not enforcedEnforced on every request
Ask AI and the MCP servers (ai)HiddenServed
Wildcard redirects (/old/*)Netlify and Cloudflare onlyReal HTTP redirects everywhere
Cheapest hostingYesNeeds a Node runtime

If your Nest app already serves the docs, you don't need server mode. mountOrbitDocs enforces access and serves Ask AI from the static build.

Turn it on

Set the mode:

orbitdocs.config.ts
export default defineConfig({
  // …
  output: { mode: 'server', basePath: '' },
});

Keep proxy.ts in the docs app root. orbitdocs init creates it:

proxy.ts
import { orbitProxy } from '@orbitdocs/next/proxy';

export default orbitProxy();

export const config = {
  matcher: ['/((?!_next/static|_next/image).*)'],
};

Build:

npx orbitdocs build

The CLI prints Server build in .next (start with `next start`).

Start it from the docs app folder, with the secrets in the environment:

ORBITDOCS_AUTH_SECRET=$(openssl rand -hex 32) npx next start -p 3001

What proxy.ts does

orbitProxy() reads two files that orbitdocs build writes: .orbitdocs/access.json (from access) and .orbitdocs/ai.json (from ai). For each request it:

  1. Returns 404 for orbitdocs-access.json and orbitdocs-ai.json, so the manifests are never served.
  2. Answers Ask AI (/_ai/*) and MCP (/mcp, /mcp/<api id>) when the config has ai.
  3. Answers sign-in routes (/_auth/*) when the config has access.
  4. Gates the page. Readers without access are sent to the login page.
  5. Lets the request through to Next.

Without access and ai, it lets every request through. A site with neither section can keep the file.

Options

OptionTypeDefaultWhat it does
appDirstringprocess.cwd()Folder that holds .orbitdocs/. Set it when next start runs from another folder.
publicUrlstringThe request's originPublic origin, such as https://docs.acme.com, when a proxy in front rewrites the Host header. Used for sign-in callback URLs.
proxy.ts
export default orbitProxy({ publicUrl: 'https://docs.acme.com' });

Ship the .orbitdocs folder

The proxy reads .orbitdocs/access.json and .orbitdocs/ai.json at runtime. Copy that folder along with .next/ wherever you run the server. Without it the proxy finds no rules and serves every page.

Environment variables

Secrets are read from the server's environment at request time, never from the build:

VariableNeeded for
ORBITDOCS_AUTH_SECRET (or your access.session.secretEnv)Signing reader sessions. 32 or more characters.
Each provider's clientSecretEnvSSO sign-in.
jwtSecretEnv, secretKeyEnv of appSessionSome app-session adapters.
access.personalization.secretEnvSigning the personalization hook call.
ai.apiKeyEnvAsk AI and MCP.

The full list with defaults is on Environment variables.

Run it in a container

The CLI's docker target only covers static builds. For server mode, build first and copy the docs app into a Node image:

docs/Dockerfile
FROM node:22-bookworm-slim
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["npx", "next", "start", "-p", "3000"]
docs/.dockerignore
node_modules
out

The image installs its own node_modules so native packages match Linux. .next/, openapi/ and .orbitdocs/ come from your build.

cd docs
npx orbitdocs build          # writes .next/, openapi/ and .orbitdocs/
docker build -t acme-docs .
docker run -p 3000:3000 -e ORBITDOCS_AUTH_SECRET=… acme-docs

Build on the host or in CI, not inside the image: extraction needs your Nest app, which is outside the docs folder.

Differences from a static build

  • Redirects are real 301/302 responses, wildcards included.
  • URLs keep their trailing slash (/quickstart/), the same as static.
  • orbitdocs deploy accepts only --target vercel in this mode.
  • orbitdocs publish refuses server builds. The platform hosts static builds and adds its own server.

Next steps

Last updated on

On this page