Kura Docs
Advanced / Deploy

Deploy

Cloudflare Workers

To publish the site to Cloudflare Workers, run:

kura deploy

kura deploy freezes your content and search index, builds a portable Worker bundle, and ships it to Cloudflare. The bundle is filesystem-free, so it runs directly on edge nodes — and the same codebase deploys to Vercel or Deno Deploy with no changes.

To point a custom domain at the Worker, set it in june.config.ts:

export default defineJune({
  // …
  deploy: { target: "workers", name: "my-docs", domain: "docs.example.com" },
});

The default target. kura deploy produces a Worker bundle and uploads it; a domain becomes a Workers custom domain.

Continuous deployment

kura deploy is the same command everywhere — run it from your machine or any CI (GitHub Actions, a container, etc.) and it builds and ships in one step. Nothing platform-specific to configure.

If you instead wire up Cloudflare's Git integration (Workers Builds — Cloudflare builds and deploys on every push), split the two steps:

  • Build command: npm run build
  • Deploy command: npx wrangler deploy -c dist/wrangler.jsonc
  • Node version: the scaffold ships an .nvmrc (≥ 22.18) — the build imports a generated .ts module, which needs Node's type stripping.

kura build writes the Worker config to dist/wrangler.jsonc, so the deploy step points wrangler at it. Leave the project root unchanged — only the deploy step looks into dist/.

Search on the edge

By default, kura deploy builds a semantic search index: it embeds your docs at build time (locally, via Xenova/bge-m3) and freezes the vectors into the Worker bundle. Semantic search at the edge then needs a runtime embedder to embed queries — back it with Cloudflare Workers AI (@cf/baai/bge-m3).

If your deploy has no runtime embedder, pass --no-embed to skip the index entirely:

kura deploy --no-embed

Search then degrades to a lexical scan — a zero-dependency match that runs anywhere, including Workers, with nothing to provision.

One codebase, from local to the edge: semantic search is the default, and --no-embed falls back to a zero-setup lexical scan — so a missing embedder is never a deployment blocker.