Skip to Content
Documentation

Deployments

Bun

Develop, build and run a VitNode app or API on Bun instead of Node.js - TanStack Start through Nitro's bun preset, and Hono on Bun.serve.

VitNode runs on Bun from the first dev to production. The TanStack Start app runs Vite on Bun and builds with Nitro's bun preset, and a standalone Hono API runs its TypeScript straight on Bun.serve - no compile step. Same app, same plugins, one runtime fewer to install on the server.

Before you begin

  • Bun installed on your machine and on the server. Node.js is optional: when it is missing, bun run stands in for it, so the vitnode CLI still runs.
  • A PostgreSQL database, as for any VitNode deployment.

Create a Bun app

Answer Bun to "Where to deploy?" when you create the app. The option shows up only when Bun is installed, and it is the default when you pick Bun as the package manager.

Create a Bun app
bun create vitnode-app@canary --deploy bun

Any package manager can install the packages; the runtime is a separate choice. Here is what the generator changes compared with a Node app:

WhereNode appBun app
App dev / buildvitnode dev, vitnode buildbun --bun vitnode dev, bun --bun vitnode build
App vite.config.tsNitro's default Node servernitro: { preset: "bun" }
API entry@hono/node-server and wsexport default { fetch, websocket } with hono/bun
API buildtsc into distnone - Bun runs src/index.ts
package.json"vitnode": { "runtime": "node" }"vitnode": { "runtime": "bun" }

start stays vitnode start in both. It reads the build's preset (or the API's vitnode.runtime) and starts the server with Bun or Node to match.

Move an existing app to Bun

Build the app for Bun

Set Nitro's preset in the app's vite.config.ts:

vite.config.ts (excerpt)
vitnode({
  nitro: {
    preset: 'bun',
    rollupConfig: { external: [/^@sentry\//] },
  },
  webSocket: true,
})

Run Vite on Bun

Prefix the app's dev and build scripts with bun --bun, so Vite and the SSR code run on Bun rather than on Node:

package.json (excerpt)
{
  "scripts": {
    "dev": "bun --bun vitnode dev",
    "build": "bun --bun vitnode build",
    "start": "vitnode start"
  },
  "vitnode": { "runtime": "bun" }
}

Serve a standalone API with Bun

Skip this step for a single app - its API is part of the build above.

A standalone API exports Bun's server object instead of calling serve from @hono/node-server. Copy the WebSocket route for Bun, drop the API's build script and set "vitnode": { "runtime": "bun" } in its package.json. vitnode dev then runs bun --hot src/index.ts and vitnode start runs bun src/index.ts. You can remove @hono/node-server and add @types/bun to devDependencies.

Build and run

Deploy exactly as in Self-hosted, with the same four commands:

Deploy on Bun
bun install --frozen-lockfile
bun run build
bun run db:migrate
bun start
Bun reads .env on its own

Bun loads .env before your code starts, so every variable in it applies - even one you forgot about. A REDIS_URL pointing at a Redis that isn't running makes WebSocket handshakes answer 503 until Redis is up. Comment out what you don't use.

Check the result

vitnode start prints Running once the server accepts connections. To be sure it is Bun answering, list the processes: the server shows up as bun .output/server/index.mjs for an app, or bun src/index.ts for an API.