11. Runtime
Bun by default for everything that runs on your machine or in CI. Node for the deployed function runtime (D27). Node stays supported everywhere because the default must not become a requirement.
M0 verified Bun locally end to end: bun run --bun next build, production serving, routing middleware, and ISR revalidation all passed under Bun 1.3.14 with Next 16.2.12. Bun as the Vercel function runtime could not be verified against a live deployment, so it is an opt-in rather than the default until it is.
The matrix
| Stage | Runtime | How |
|---|---|---|
| install | Bun | zero-config on Vercel |
| dev | Bun | bun run --bun next dev |
| build | Bun | bun run --bun next build |
| function runtime | Node | Vercel default, no config |
| function runtime, opt-in | Bun | {"bunVersion": "1.x"} in vercel.json, verify on a preview first |
| middleware | Node | Next 16 Proxy defaults to the Node runtime |
| tests | Bun | bun test |
| CLI | Bun | native TypeScript, no transpile step, compiles to a single binary |
// vercel.json — shipped default. Add "bunVersion": "1.x" to opt in to the Bun function runtime.
{
"$schema": "https://openapi.vercel.sh/vercel.json"
}
// package.json
{
"scripts": {
"dev": "reeve route:sync && bun run --bun next dev",
"build": "reeve route:sync && bun run --bun next build",
"test": "bun test",
"check": "reeve doctor"
}
}
Known gaps on the Bun runtime
- No automatic source maps.
- No bytecode caching.
- No request metrics on
node:httpandnode:https. Metrics viafetchwork on both runtimes.
None of these block v1. All three are worth stating in the docs, because "my stack traces got worse" is a support ticket you can prevent with one sentence.
Why Bun
- Native TypeScript execution, so the CLI and generators have no transpile step and no
tsx
dependency.
bun build --compileproduces a single-binary CLI, which makesreeveinstallable without a
Node toolchain.
bun testis fast enough to run on save, which is the only property that decides whether tests
actually get written.
- Install speed matters more than it should when a fleet of agents is running CI on every package.
The constraint
**@avelon/core and @avelon/orm may never import bun:*.** Enforced by the Bailiff rule no-bun-in-core.
Three reasons:
1. A self-hoster running Node must be able to use this. 2. Adapters other than Next may target runtimes where Bun is not available. 3. The day Bun and a deployment platform disagree about something you need, you want an exit that is a configuration change rather than a rewrite.
Bun-specific code is allowed in @avelon/cli (which is always run locally or in CI) and in driver packages that explicitly declare a Bun requirement in their capabilities.
Node fallback
Everything runs on Node 22 or later with tsx for the CLI. It is slower and it is supported. CI runs the full suite on both runtimes, because a Node incompatibility introduced quietly is far more expensive to find later than to prevent now.