Bun
Deploy Bun apps on Skiffly with Railpack — how a `bun.lock` changes the build, `bun run start` versus `bun src/index.ts`, `Bun.serve` on `PORT`, Elysia and Hono, databases and the differences from the Node path.
Railpack has no separate Bun provider: a repository with package.json is handled by the Node provider, and a Bun lockfile switches the package manager and the runtime commands to Bun. Everything on the Node.js page about ports, variables, migrations and health checks applies; this page lists what is different.
What Railpack detects#
| Looks at | Effect |
|---|---|
bun.lock or bun.lockb (or "packageManager": "bun@1.x") | dependencies installed with bun install --frozen-lockfile; scripts run with bun run |
scripts.start | start command bun run start |
no start script: main in package.json, then index.ts/index.js | bun <file> |
scripts.build | bun run build in the build step |
.bun-version, packageManager, mise.toml/.tool-versions | the Bun version (default: latest stable) |
engines.node, .nvmrc | Node is installed alongside for native modules that compile with node-gyp |
Bun runs TypeScript directly, so a typical service has no build step at all: "start": "bun src/index.ts".
Port#
Bun.serve({
port: Number(process.env.PORT ?? 3000),
hostname: "0.0.0.0",
fetch: (req) => new Response("ok"),
});Bun.serve defaults to 0.0.0.0 and reads PORT only when you pass it; Elysia (app.listen(process.env.PORT ?? 3000)) and Hono (export default { port: Number(process.env.PORT ?? 3000), fetch: app.fetch }) follow the same rule. Skiffly sets PORT to the service port (8080 when unset).
Databases#
skiffly deploy --template postgres
skiffly variables set 'DATABASE_URL=${{Postgres.DATABASE_URL}}'Bun.sql (built-in Postgres client, reads DATABASE_URL automatically), postgres.js, Drizzle with bun-sql or postgres.js, and Prisma all work. Prisma's engine binaries need prisma generate in the build ("build": "prisma generate"); the query engine runs under Bun without extra flags. Redis: Bun.redis / ioredis with REDIS_URL=${{Redis.REDIS_URL}}.
Migrations: no release phase — sh -c "bunx drizzle-kit migrate && bun src/index.ts" as the start command, or skiffly ssh -- bunx drizzle-kit migrate as a one-off.
Differences from the Node path#
- Dev dependencies are installed (Railpack needs them for
buildscripts). AddRAILPACK_PRUNE_DEPS=trueto drop them from the image. bun installhonourstrustedDependenciesfor postinstall scripts; packages with native builds (sharp,better-sqlite3) need Node present, which Railpack adds whenengines.nodeis declared.- Workspaces (
workspacesinpackage.json) install from the root; set the start command tobun run --filter api startorbun apps/api/src/index.tsfor a monorepo. bun build --compilesingle binaries are unnecessary; if you do produce one in the build step, set the start command to./app.
Health check#
Workers without an HTTP server fail the rollout probe (see Node.js); Bun.serve with a /healthz route costs one line. Add a /healthz route and set the healthcheck path so rollouts switch traffic only after the new container answers.
Common problems#
| Symptom | Fix |
|---|---|
error: lockfile had changes, but lockfile is frozen | run bun install locally and commit bun.lock |
FAILED after 5 minutes, log shows Listening on localhost:3000 | pass port: process.env.PORT and hostname: "0.0.0.0" |
Railpack runs npm install instead of Bun | no Bun lockfile in the repository: commit bun.lock or set packageManager |
Cannot find module "node:…" at runtime | an unimplemented Node API in your Bun version: pin a newer Bun with .bun-version |
| Native module fails to build | add engines.node so Node/node-gyp are available, or pick a pure-JS alternative |
Example#
import { defineSkiffly, github, postgres, project, service } from "@skiffly/config";
export default defineSkiffly(() => {
const db = postgres("db");
const api = service("api", {
source: github("acme/bun-api"),
start: "bun src/index.ts",
healthcheck: "/healthz",
port: 3000,
env: { DATABASE_URL: db.env.DATABASE_URL, RAILPACK_PRUNE_DEPS: "true" },
domain: true,
});
return project("bun-api", { resources: [db, api] });
});Landing: /deploy/bun