Skip to main content

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:

ServiceUsed forFound through
The main Apiome app (apiome-ui)Sign-in: every guarded page sends an anonymous visitor to the main app's /loginNEXT_PUBLIC_MAIN_APP_URL
apiome-restProjects, versions, classes, properties, paths, primitives, the Release Center's control planeREST_API_BASE_URL at runtime (NEXT_PUBLIC_REST_API_BASE_URL at build time)
SpireScribe content and coverage, the LLM gateway, Slate sites and buildsSPIRE_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 actionsDATABASE_URL, the same value as apiome-ui
Ollama (optional)The canvas AI buttons and the chat panelOLLAMA_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 argumentDefaultWhat it sets
NEXT_PUBLIC_MAIN_APP_URLhttps://main.apiome.devThe main app, where sign-in happens
NEXT_PUBLIC_STUDIO_URLhttps://suite.apiome.devThis deployment's own public URL
NEXT_PUBLIC_API_URLhttps://api.apiome.devThe public API origin
NEXT_PUBLIC_REST_API_BASE_URLhttp://localhost:8000/v1Build-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:

ValueStageWhat users see
unset, 0, anything elseoff/workspace redirects to /editor. Nothing links to it; the classic designer is exactly what it was.
1previewThe 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.
defaultdefaultAs preview, plus Home's cards and the suite's deep links open the workspace instead of /editor.
onlyonly/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.