Beta . Ortha CMS is in public beta. Every plan is free until it ends.
Features Ortha CMS on GitHub

Installation

Prerequisites, the two ways in, first boot, and upgrading afterwards.

Documents 0.13.1 Updated Edit this page Report a problem

On this page

Two ways in:

  • Scaffold an app that consumes @orthacms/* from npm. Almost everybody wants this.
  • Clone the monorepo to work on Ortha itself.

Prerequisites

VersionWhy
Node22 or newerThe CLI uses process.loadEnvFile and node --watch
PostgreSQL16 or newerThe only supported database. Tests run on 16; the generated compose file starts 17
npm10 or newerExact-pinned dependencies, no workspaces in a generated app. create-orthacms-app 0.7.0 installs cleanly on npm 10

Scaffold an app

npx create-orthacms-app my-cms
cd my-cms

The wizard asks for an app name, a database URL and an admin email, then five choices. Each has a flag, so it can run unattended:

FlagChoicesDefault
--media <id>media-local, media-s3, media-azure, media-gcs, media-vercel-blobmedia-local
--copilot <ids>copilot-anthropic, copilot-openai, or nonenone
--sso <ids>sso-oidc, sso-github, sso-saml, or nonenone
--protocols <ids>graphql, mcp, or none (REST is always on)none
--mail <id>mail-smtp, or nonenone
--yesAccept every default, ask nothing—
--no-install / --no-gitSkip npm install / git init—
# Unattended: S3 media, GraphQL + MCP, SMTP mail
npx create-orthacms-app my-cms --yes \
  --media media-s3 --protocols graphql,mcp --mail mail-smtp

Full reference: the CLI.

What you get

my-cms/
├── package.json          one package — not npm workspaces
├── docker-compose.yml    Postgres for local development
├── .env                  written by the scaffolder
├── tsconfig.json         references the four apps
└── apps/
    ├── server/           orthacms.config.ts, config/*.ts, src/{main,plugins}.ts,
    │                     src/content/ (your types), migrations/, drizzle.config.ts
    ├── admin/            index.html, vite.config.mts, src/{main,plugins}.ts
    ├── server-e2e/       Jest + supertest, against a real Postgres
    └── admin-e2e/        Playwright, with /api mocked
  • One package.json, on purpose. Workspaces could resolve two copies of @orthacms/design-system — two React contexts, and a UI that silently stops working.
  • Tests work out of the box. npm test runs the unit tests (Jest for the server, Vitest for the admin); npm run e2e runs both end-to-end suites.
  • No content types yet — but their plumbing is there: the generated manifest, the drizzle config and a baseline migration. Add types on the admin’s Content Model page (the schema builder, with SCHEMA_BUILDER=true) or by hand; the quick start shows both.

Configure

The scaffolder writes .env:

DATABASE_URL=postgresql://orthacms:orthacms@localhost:5432/my_cms
ORTHACMS_ROOT_ADMIN_EMAIL=admin@example.com
ORTHACMS_ROOT_ADMIN_PASSWORD=…   # generated, printed once
  • DATABASE_URL is the only variable a default app refuses to boot without.
  • The root admin is provisioned on boot, idempotently. Keep both ORTHACMS_ROOT_ADMIN_* values set until you have signed in.
  • With mail chosen, MAIL_PROVIDER=smtp also requires APP_URL, MAIL_FROM and SMTP_HOST.

Everything else is optional — see configuration.

Migrate and boot

docker compose up -d   # if you have no Postgres
npm run migrate        # orthacms migrate
npm run dev            # orthacms dev — API :3000, admin :4200
npm scriptRunsDoes
npm run devorthacms devtsc --watch + node --watch + Vite
npm run migrateorthacms migrateBuild, then apply every plugin’s migrations in plugin order
npm run generateorthacms generateGenerate SQL for your content tables
npm run content:syncorthacms content syncRewrite the content manifest, src/content/index.ts
npm run buildorthacms buildServer into dist/server, admin into dist/admin
npm startorthacms startOne process serving the API and the built admin from one origin
npm run studioorthacms studioDrizzle Studio on the live database

Verify the install

curl -s http://localhost:3000/reference/json | head -c 200   # the raw OpenAPI document
# the interactive Scalar UI is at http://localhost:3000/reference

The reference is generated from the running app and served outside production. Your content types appear in it once registered. Set API_DOCS=true to publish it from a deployment.

Upgrading

Every @orthacms/* package is released in lockstep and pinned exactly. Move them all at once:

npm install @orthacms/content-server@0.7.0 @orthacms/content-admin@0.7.0 # …and every other @orthacms/* package
npx orthacms migrate
npm ls @orthacms/design-system react   # must show exactly one of each

Upgrade the whole set, or none of it

A partial upgrade can leave two copies of a shared package in node_modules — two React contexts, and an admin that silently stops working. Nothing errors.

The one-command bump and per-release notes are on upgrading.

Working on Ortha itself

git clone https://github.com/ortha-source/ortha-cms
cd ortha-cms
npm install
cp .env.example .env
docker compose up -d
npx nx run server:db:migrate
npm run dev
  • Packages resolve from source — a change in a package is live with no build step.
  • The Nx targets call the same functions the orthacms binary does.

See project structure.