Deploy with rapidoc serve
A portal with auth is served by rapidoc serve (@rapidoc/server). It serves the build, signs readers in through your provider, and enforces the access rules on every page, spec, file and search result.
npm install -D @rapidoc/cli @rapidoc/portal @rapidoc/servernpx rapidoc buildRAPIDOC_SESSION_SECRET=... RAPIDOC_AUTH_CLIENT_SECRET=... RAPIDOC_PUBLIC_URL=https://developers.acme.com \ npx rapidoc serve dist --port 8080- Stateless: the session is an encrypted cookie, so any number of instances can run behind a load balancer. No database.
- Small: it needs only Node, the CLI,
@rapidoc/serverand the build. Not@rapidoc/portal. - Agents too: the APIs and workflows are MCP tools at
/mcp, under the same rules. See MCP server for agents.
Environment variables
Section titled “Environment variables”| Variable | Required | Meaning |
|---|---|---|
RAPIDOC_SESSION_SECRET |
with auth |
Encrypts the session cookie. 32 or more characters, the same on every instance. Changing it signs everyone out. |
RAPIDOC_AUTH_CLIENT_SECRET |
confidential clients | Leave unset for a public client (PKCE only). |
RAPIDOC_PUBLIC_URL |
recommended | The portal’s external URL: the redirect URL, and whether cookies are Secure. Default site.url, else the request’s host. |
RAPIDOC_AUTH_ISSUER, RAPIDOC_AUTH_CLIENT_ID, RAPIDOC_AUTH_AUDIENCE |
no | Override auth.issuer, auth.clientId, auth.audience. |
RAPIDOC_MOUNT_PATH |
no | Path requests arrive under, when a proxy strips part of the portal’s path. --mount-path wins. Default: the path of site.url. |
PORT, HOST |
no | Default 8080 and 0.0.0.0. --port and --host win. |
<SCHEME>, API_KEY |
no | API keys and HTTP Basic credentials for /mcp tool calls, named by the spec’s security scheme id. See MCP server for agents. |
The server refuses to start, with the reason, when a required value is missing or the provider can’t be discovered.
Behind a proxy that strips part of the path
Section titled “Behind a proxy that strips part of the path”A portal with site.url: https://acme.com/sales/developers is built and served under /sales/developers. When a gateway or ingress routes /sales/* to the server and strips /sales, requests arrive as /developers/.... Tell the server:
npx rapidoc serve dist --mount-path /developersLinks, redirects and the sign-in callback keep the full path the browser sees, /sales/developers/.... Without a proxy, the server answers under the path of site.url by itself.
Docker
Section titled “Docker”FROM node:22-alpine AS buildWORKDIR /portalCOPY package*.json ./RUN npm ciCOPY . .RUN npx rapidoc build
FROM node:22-alpineWORKDIR /portalCOPY package*.json ./RUN npm ci --omit=dev && npm install --no-save @rapidoc/cli @rapidoc/serverCOPY --from=build /portal/dist ./distENV PORT=8080EXPOSE 8080CMD ["npx", "rapidoc", "serve", "dist"]Health check for load balancers and Kubernetes: GET /_rapidoc/health.
The build output
Section titled “The build output”With auth, rapidoc build writes:
| Path | Holds |
|---|---|
dist/public/ |
Everything anyone may read. Contains no protected content, so it can sit on a CDN. |
dist/protected/ |
Pages and files behind a rule. |
dist/rapidoc-server.json |
The rules and zones the server enforces. |
Responses that depend on the session are Cache-Control: private, no-store with Vary: Cookie, so a CDN in front never caches a protected page for the wrong reader.
Without auth
Section titled “Without auth”rapidoc serve also serves a plain static build, which is handy for a local check of the production build:
npx rapidoc build && npx rapidoc serve dist