Chronicle

Build and serve

chronicle build
chronicle start

build writes the production site. start serves what build produced — it does not build for you, so the order matters. chronicle serve runs both, which is handy locally and wrong in a deployment, where you want the build to happen once and the server to start many times.

Two kinds of output

The preset decides which you get, and it is the only decision that really matters here.

A server build produces a small server. Pages are rendered per request, which is what makes search, image resizing and the API request tester work. This is the default.

A static build produces a single-page app: one index.html, a JavaScript bundle, and a small JSON file per page that the app fetches as the reader navigates. There is no process to run and nothing to keep alive.

preset: static

Or per build:

chronicle build --preset static

What you give up going static

There is no server, so the parts that needed one change:

Server buildStatic build
Page HTMLRendered per request, works with JavaScript offOne shell; pages filled in by JavaScript
SearchQueried per requestWhole index downloaded, searched in the browser
ImagesResized on demand, then cachedResized once, during the build
API request testerProxies through your serverNot available
Health and readiness/api/health, /api/readyNot available
RedirectsServed by ChronicleYour host has to do them

Everything else is generated either way: navigation, versions, the API reference, markdown URLs, llms.txt, the sitemap and social cards.

The first row matters most. A server build sends finished HTML, so a crawler or a reader with JavaScript off gets the page. A static build sends a shell, so they get very little. If search ranking matters to you, that is the argument for a server build.

Pick static for a small or medium site you want on a CDN with nothing to operate. Pick a server build for a large site, when search needs to stay fast, or when the request tester matters.

Where the output goes

PresetOutput directory
unset, node-server, cloudflare.output/
static, cloudflare-pages, github-pages.output/public/
vercel, vercel-static.vercel/output/

A static host has to send unknown paths to index.html, because every page shares that one file. Getting this wrong is the usual reason a deep link 404s while the home page works — see Deploy.

Gitignore whichever applies. chronicle init adds .output for you.

Build failures

The build stops on a page it cannot parse, and names the file and the line. This is deliberate — a docs site that silently drops a broken page is worse than one that refuses to build.

The usual cause is MDX being stricter than markdown about < and {, which it reads as the start of a component or an expression. Wrap the character in backticks.

In CI

bun install
bun run chronicle build --preset static

Then publish the output directory. The build needs no network access beyond installing packages, and no services.

If your build machine is not the machine that serves the site, remember start needs the .output/ directory that build wrote — copy it, or build on the host.

Next

  • Deploy — putting the output on a host
  • Docker — running from the container image
  • Monitoring — health checks and metrics