OrbitDocs packages are coming to npm soon. Until then, run it from the GitHub repo →
Self-hosted platform

Self-hosted platform

One place for every docs site in your company, on your own servers. Publishing, previews, a spec registry, teams, domains, analytics and an audit log.

The CLI is enough for one team publishing one site. The platform is an optional app you run yourself when many teams publish many sites. It hosts every site, builds them from Git, keeps every spec version and controls who can change what.

It is part of the same MIT-licensed repository. There is no hosted version and no paid tier.

What it adds

ProjectsEach docs site is a project, served at <project>.<your domain>.
Publishingorbitdocs publish from CI, or Git sync. The five newest production builds are kept; roll back in one click.
PreviewsEvery pull or merge request gets its own site, linked from a comment and removed when it closes.
RegistryEvery published version of every API spec, linted with Spectral. Optionally block a publish on lint errors.
TeamsOwner, admin, editor and viewer roles per project. Sign in with a password or the same SSO presets as private docs.
SCIMOkta, Entra ID and other identity providers create and deactivate users.
DomainsYour own domain per project, with HTTPS certificates issued automatically.
AnalyticsBuilt-in (no cookies), or Plausible, Umami or PostHog; plus what readers ask Ask AI and which questions the docs don't cover.
Audit logSign-ins, publishes, rollbacks, role changes, tokens, domains and provisioning.

Private docs and Ask AI work on hosted sites exactly as they do in your Nest app. The platform serves them with the same code as mountOrbitDocs.

Architecture

docker compose up starts three containers:

ContainerImageJob
postgrespostgres:17-alpineUsers, projects, deployments, the registry, analytics, the audit log and the build queue.
platformBuilt from apps/platform/DockerfileOne Node process: the API, the dashboard, site hosting and the Git sync build workers. Listens on 8080.
caddycaddy:2-alpineTerminates HTTPS on ports 80 and 443 and gets certificates on demand.
                    ┌──────────── caddy :443 ────────────┐
 docs.acme.com ────▶│ dashboard + API                     │
 payments.docs… ───▶│ hosted site (project "payments")    ├──▶ platform :8080 ──▶ postgres
 docs.payments.io ─▶│ hosted site (verified custom domain)│        │
                    └─────────────────────────────────────┘        └──▶ /data (sites, builds)

The platform decides what to serve from the Host header:

  • The platform domain (or www. in front of it) is the dashboard and API.
  • <project>.<domain> is the project's live production build.
  • <project>--<label>.<domain> is a preview, such as payments--mr-42.docs.acme.com.
  • Any other host is looked up in the project's verified custom domains.

Published sites are plain files under DATA_DIR (/data in the container). The database holds everything else.

Core ideas

  • Project. One docs site. Its slug is its subdomain and can't contain --, which is reserved for previews.
  • Deployment. One uploaded build, either production or a preview with a label like mr-42.
  • Build. A Git sync job: clone, install, build, publish. It produces a deployment.
  • Registry version. One revision of one API spec, stored when a production publish changes it.

The platform hosts static builds (output.mode: 'static'). It adds the server part itself: access checks, Ask AI, MCP and analytics.

Minimal example

Terminal
git clone https://github.com/VitraAI/OrbitDocs && cd OrbitDocs/apps/platform
cp .env.example .env        # set PLATFORM_SECRET, ADMIN_EMAIL, ADMIN_PASSWORD
docker compose up -d

Open https://localhost, sign in as the admin, create a project, then publish from your docs app:

ORBITDOCS_TOKEN=odp_… npx orbitdocs publish --platform https://localhost

Next steps

Last updated on

On this page