Build & deploy

Three build modes, from fully static to full server.

apex build spans a spectrum — a zero-JS static site, a prerendered-and-hydrated static bundle, or a deployable Node server for dynamic routes, API, MCP, and a database. Pick the most static mode your app can use.

Choosing a mode #

ModeCommandShipsUse when
Static (SSG)apex build --islandsHTML, zero framework JS (islands hydrate where present)Content sites; static routes only.
Prerendered + hydratedapex buildHTML + per-page client bundleInteractive apps that can run from a static host.
Server targetapex build --server + apex startClient bundles + SSR modules + a Node serverDynamic routes, API/MCP, or a database in production.

1 · Static site (SSG) — apex build --islands #

Prerenders every static route to dist/**/index.html with zero framework JS — islands hydrate lazily where present. Deploy dist/ to any static host.

apex build --islands
# → dist/index.html, dist/about/index.html, …  (static-first)

2 · Prerendered + hydrated — apex build #

Prerenders each page to HTML and builds a per-page client bundle (hashed, with a shared runtime chunk). Pages are server-rendered for first paint, then hydrate — fully interactive from a static dist/.

apex build
# → dist/  (prerendered HTML + hashed client bundles)
Dynamic routes need the server target

apex build and --islands prerender only static routes and report any dynamic [param] routes they skipped. To serve those in production, use --server below.

3 · Server target — apex build --server + apex start #

Builds client bundles and SSR modules plus a run manifest (apex-manifest.json), producing a deployable Node server:

apex build --server     # → dist/ (client assets + dist/server/*.mjs + apex-manifest.json)
apex migrate            # apply DB migrations (deploy step)
apex start              # run the production server (no Vite)

The production server (apex start, verified end-to-end) serves:

  • Dynamic routespages/blog/[slug].alpine rendered per request.
  • API + MCPserver/api/* as REST endpoints and MCP tools at /mcp.
  • Static assets — hashed client bundles with long-cache headers.
  • Any database — SQLite/Turso/Supabase/Neon via @apex-stack/data.

Databases in production #

Local SQLite is great for a Node host. For serverless/edge (e.g. Cloudflare Workers), pair with a fetch-based driver — Turso (libSQL) or Supabase/Neon (Postgres) — since native SQLite can't run on edge. Swap drivers in one line:

await createDb({ driver: 'libsql', url: process.env.TURSO_URL })       // Turso
await createDb({ driver: 'postgres', url: process.env.DATABASE_URL })   // Supabase / Neon

Run apex migrate as a deploy step (with the matching --driver/--url). See Data → Drivers.

Hosting targets #

HostBest modeNotes
Netlify / Cloudflare Pages / S3SSG or prerenderedServe dist/ as static files.
GitHub PagesSSG or prerenderedStatic-only; keep asset paths relative (this docs site does).
Node host (Fly, Render, a VM)Server targetapex build --server + apex start; local SQLite works.
Serverless / edgeServer targetPair with a fetch-based DB driver (Turso / Supabase / Neon).
On the roadmap

Nitro deploy presets (Vercel/Netlify/Workers adapters) — today apex start targets Node. Also planned: island-mode client bundling in the built output, and streaming SSR.