Deploy the studio
The studio is the designer workspace of the private-suite repository: a Next.js application that
serves the whole Suite — the Designer canvas and workspace, Authoring, Scribe, Slate and the
hosted-plan pages. In production it runs as the apiome-suite Docker image at
suite.apiome.dev, beside the main Apiome application at
main.apiome.dev.
What it talks to
The studio never holds data of its own. Every page reads from, and writes to, one of these services:
| Service | Used for | Found through |
|---|---|---|
The main Apiome app (apiome-ui) | Sign-in: every guarded page sends an anonymous visitor to the main app's /login | NEXT_PUBLIC_MAIN_APP_URL |
apiome-rest | Projects, versions, classes, properties, paths, primitives, the Release Center's control plane | REST_API_BASE_URL at runtime (NEXT_PUBLIC_REST_API_BASE_URL at build time) |
| Spire | Scribe content and coverage, the LLM gateway, Slate sites and builds | SPIRE_API_BASE_URL at runtime (NEXT_PUBLIC_SPIRE_API_BASE_URL at build time) |
| The Apiome database (Postgres) | Sessions, and the Paths canvas's server actions | DATABASE_URL, the same value as apiome-ui |
| Ollama (optional) | The canvas AI buttons and the chat panel | OLLAMA_BASE_URL |
The studio's own /api/* routes are a backend-for-frontend: /api/llm/*, /api/docs/* and
/api/sites/* forward to Spire, /api/slate/* and /api/rest/* forward to apiome-rest, each with
a service bearer the studio mints from the shared sign-in secret (see
Authentication).
Build the image
The Dockerfile builds from the Apiome monorepo root, with private-suite checked out inside it
as a sibling of apiome-ui — the studio imports shared code from apiome-ui, and the image's
yarn install needs both lockfiles:
# from the apiome monorepo root, with private-suite/ checked out inside it
docker build -f private-suite/designer/Dockerfile -t apiome-suite:latest .
private-suite/designer/build-docker.sh runs the same build and tags the result
apiome-studio:latest; the Suite workflow (.github/workflows/suite.yml) pushes it to the
registry as apiome-suite, tagged with the designer package version, latest and the commit SHA.
Public URLs are inlined at build time. Pass them as build arguments — setting them when the container starts has no effect:
| Build argument | Default | What it sets |
|---|---|---|
NEXT_PUBLIC_MAIN_APP_URL | https://main.apiome.dev | The main app, where sign-in happens |
NEXT_PUBLIC_STUDIO_URL | https://suite.apiome.dev | This deployment's own public URL |
NEXT_PUBLIC_API_URL | https://api.apiome.dev | The public API origin |
NEXT_PUBLIC_REST_API_BASE_URL | http://localhost:8000/v1 | Build-time fallback for apiome-rest |
The image always builds with NEXT_PUBLIC_APP_SURFACE=studio, so its routes are unprefixed
(/workspace, /authoring/scribe …); the suite-embedded /ade/studio/* routes are served by the
main app and redirect here. See Routes.
Run the container
The runtime image runs as the nextjs user, listens on port 3003, and starts the standalone
server:
docker run --rm -p 3003:3003 \
-e REST_API_BASE_URL=http://rest:8000/v1 \
-e SPIRE_API_BASE_URL=http://spire:8099/v1 \
-e DATABASE_URL=postgresql://postgres:pass@db:5432/apiome \
-e BETTER_AUTH_SECRET=... \
-e BETTER_AUTH_URL=https://suite.apiome.dev \
-e BETTER_AUTH_COOKIE_DOMAIN=.apiome.dev \
apiome-suite:latest
Set the runtime variables from the environment reference. The three
BETTER_AUTH_* values are the ones that make a studio sign-in a suite sign-in; get them wrong and
the only symptom is a second login prompt, so the server logs a startup warning naming the
offending variable (Authentication).
Run it from source
From the private-suite root:
yarn install
cp designer/.env.example designer/.env # then edit it
yarn dev:designer # http://localhost:3003
designer/scripts/run.sh dev|start loads .env into the environment first (Next.js does not read
PORT and HOSTNAME from .env on its own) and defaults the port to 3003. The example file sets
PORT=3004, so a copied .env listens there until you change it.
In development, unported /api/* routes can fall back to a running apiome-ui
(STUDIO_BFF_FALLBACK_URL); leave that unset in production, where the studio serves every BFF route
itself.
Roll out the unified workspace
The workspace at /workspace — schemas and paths in one domain-scoped surface — is staged behind
one build-time variable, NEXT_PUBLIC_STUDIO_UNIFIED_WORKSPACE:
| Value | Stage | What users see |
|---|---|---|
unset, 0, anything else | off | /workspace redirects to /editor. Nothing links to it; the classic designer is exactly what it was. |
1 | preview | The workspace is served. /editor and /paths carry a dismissible Try the new workspace banner, the suite dropdown gains a Workspace · Preview destination, and the workspace links back with Classic designer. |
default | default | As preview, plus Home's cards and the suite's deep links open the workspace instead of /editor. |
only | only | /editor and /paths redirect into /workspace, carrying the project, version, domain and selection; the workspace's top-bar link becomes Code & exports. |
Roll it forward one environment at a time — 1 in developer .env files, then 1 everywhere, then
default, and only once the parity checklist is signed off. Rolling back is a redeploy with the
previous value; nothing is deleted. The Workspace page describes each stage as
a user sees it.
Health and checks
The studio has no /healthz of its own; the Suite workflow's smoke tests and the nightly
Studio E2E run (see Release process) exercise it as a user does. At
startup the server runs the boot checks in src/instrumentation.ts and warns and continues —
missing-secret, missing-base-url, host-only-cookie, origin-outside-cookie-domain,
legacy-nextauth-cookie-env, unsigned-bearer, legacy-nextauth-secret-mismatch and
malformed-app-url. Read the log after a deploy.