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 #
| Mode | Command | Ships | Use when |
|---|---|---|---|
| Static (SSG) | apex build --islands | HTML, zero framework JS (islands hydrate where present) | Content sites; static routes only. |
| Prerendered + hydrated | apex build | HTML + per-page client bundle | Interactive apps that can run from a static host. |
| Server target | apex build --server + apex start | Client bundles + SSR modules + a Node server | Dynamic 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)
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 routes —
pages/blog/[slug].alpinerendered per request. - API + MCP —
server/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 #
| Host | Best mode | Notes |
|---|---|---|
| Netlify / Cloudflare Pages / S3 | SSG or prerendered | Serve dist/ as static files. |
| GitHub Pages | SSG or prerendered | Static-only; keep asset paths relative (this docs site does). |
| Node host (Fly, Render, a VM) | Server target | apex build --server + apex start; local SQLite works. |
| Serverless / edge | Server target | Pair with a fetch-based DB driver (Turso / Supabase / Neon). |
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.