Bun Runtime skill for Codex

Adopt Bun without surprise Node fallbacks. Codex adopts Bun one layer at a time, package manager, tests, then runtime, and checks what still runs on Node along the way. It commits the text bun.lock, lists trustedDependencies for packages that need postinstall, keeps tsc in CI because Bun strips types without checking, and serves HTTP with Bun.serve routes plus a clean SIGTERM stop. Known traps like auto-loaded .env files are called out.

  • Backend & APIs
  • $bun-runtime
  • 183 lines
  • 2 files
  • ~1,850 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- bun-runtime

Installs to ~/.agents/skills/bun-runtime/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $bun-runtime, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Uses Bun as runtime, package manager, bundler and test runner - bun install with the text bun.lock and trustedDependencies, workspaces, Bun.serve with routes, Bun.file, bun:sqlite and Bun.sql, bun test with mocks and coverage, bun build --compile, and Docker images on oven/bun. Covers Node compatibility traps such as scripts that silently run on Node, unsupported native addons, automatic .env loading and missing type checks. Use when moving a Node project to Bun, starting a Bun service, or debugging behaviour that differs between Bun and Node.

Use it when

  • Migrating a Node.js project to Bun as package manager, test runner or runtime
  • Starting a new HTTP service or CLI on Bun
  • Behaviour differs between Bun and Node and you need to find out why
  • Producing a single-file executable with bun build --compile

Use something else when

  • The project targets Deno or Deno Deploy: use deno-apps
  • You need a framework-level API design on Hono regardless of runtime: use hono-edge-api

How to check the result

bun install --frozen-lockfile succeeds from a clean checkout, tsc --noEmit and bun test pass, and the service answers on its health route and exits cleanly on SIGTERM.

The mistake it prevents

Assuming bun run executes scripts on Bun, when tools with a node shebang still run on Node unless invoked with bun --bun.

~/.agents/skills/bun-runtime/SKILL.md

--- name: bun-runtime description: Uses Bun as runtime, package manager, bundler and test runner - bun install with the text bun.lock and trustedDependencies, workspaces, Bun.serve… license: MIT ---

Done well means: the team knows which parts run on Bun and which still run on Node, the lockfile is the text bun.lock and committed, CI installs with --frozen-lockfile, TypeScript is still type-checked by tsc, and every Node API the app depends on has been exercised under Bun by a test, not assumed compatible.

Before changing anything

  1. Read AGENTS.md, package.json (scripts, engines, workspaces, trustedDependencies), bunfig.toml and which lockfiles exist.
  2. Check the Bun version (bun --version) and pin it for the team (packageManager field, .bun-version for CI setup actions, or the Docker tag).
  3. Decide the scope with the user. Using Bun only as a package manager and test runner is low risk; switching the production runtime needs a compatibility pass.

Adoption decision

GoalDoRisk
Faster installs onlybun install, keep running on NodeLow; lifecycle scripts differ
Faster testsbun test for unit testsMedium; Jest/Vitest APIs not 1:1
Run scripts and toolingbun run, bunxLow; scripts still use Node unless --bun
Production runtimebun run src/index.ts in the containerHighest; test every Node API path
Single binary CLIbun build --compileLow for pure TS; native addons need care

Package management

bun install                       # creates bun.lock (text, Bun 1.2+); commit it
bun install --frozen-lockfile     # CI: fail if package.json and lockfile disagree
bun add zod; bun add -d @types/bun
bun pm trust <pkg>                # allow a dependency's postinstall script
bunx prisma generate              # like npx
  • Bun does not run dependency lifecycle scripts (postinstall) unless the package is in trustedDependencies or on Bun’s default allow-list. Missing native builds (sharp, bcrypt, prisma engines) usually trace back to this.
  • Migrating from an old binary bun.lockb: run bun install --save-text-lockfile (or any install on Bun 1.2+), commit bun.lock, delete bun.lockb.
  • Migrating from npm, yarn or pnpm: bun install reads the existing lockfile once to preserve versions. Delete the old lockfile only after bun.lock is committed and CI is green.
  • Workspaces: declare them in package.json workspaces; bun install links them. Check the [install] linker setting in bunfig.toml (hoisted vs isolated) before debugging “module not found” in a monorepo.
  • Installs need network; in a Codex sandbox with network off, request approval.

Serving HTTP

const server = Bun.serve({
  port: Number(process.env.PORT ?? 3000),
  routes: {
    "/healthz": new Response(null, { status: 204 }),
    "/api/orders/:id": {
      GET: async (req) => {
        const order = await findOrder(req.params.id);
        return order ? Response.json(order) : Response.json({ error: "not found" }, { status: 404 });
      },
    },
    "/api/orders": {
      POST: async (req) => Response.json(await createOrder(await req.json()), { status: 201 }),
    },
  },
  fetch() {
    return Response.json({ error: "not found" }, { status: 404 });
  },
  error(err) {
    console.error(err);
    return Response.json({ error: "internal" }, { status: 500 });
  },
});

process.on("SIGTERM", async () => {
  await server.stop();     // stop accepting, let in-flight requests finish
  process.exit(0);
});

routes needs Bun 1.2.3+. Frameworks built on Web standard Request/Response (Hono, Elysia) run natively; Express and Fastify run through Bun’s node:http layer and work for most apps, but measure rather than assume a speedup.

Bun-native APIs

NeedAPINote
Read a fileawait Bun.file(path).text() / .json() / .arrayBuffer()Lazy; exists() before reading
Write a fileawait Bun.write(path, data)Accepts strings, blobs, responses
SQLiteimport { Database } from "bun:sqlite"Synchronous; use prepared statements
Postgresimport { sql } from "bun" tagged templateParameterises interpolations; check driver features you rely on
Password hashingBun.password.hash / verifyargon2id by default
Shell scriptsimport { $ } from "bun"Interpolations are escaped

Using Bun-only APIs ties the code to Bun. Keep them behind a small module if the project might need to run on Node again.

Testing

import { describe, expect, it, mock, beforeEach } from "bun:test";

const sendEmail = mock(async () => ({ id: "msg_1" }));
mock.module("./email", () => ({ sendEmail }));

describe("createOrder", () => {
  beforeEach(() => sendEmail.mockClear());
  it("sends a confirmation", async () => {
    const { createOrder } = await import("./orders");
    await createOrder({ sku: "A1", quantity: 1 });
    expect(sendEmail).toHaveBeenCalledTimes(1);
  });
});
bun test                    # finds *.test.ts, *_test.ts, *.spec.ts
bun test --coverage
bun test --watch
  • mock.module must run before the module under test is imported; use a dynamic import() after mocking, or a preload script in bunfig.toml ([test] preload).
  • Jest globals and many matchers work, but timers, module mocking and snapshot formats differ. Port a few test files first and compare results with the old runner.
  • DOM tests need happy-dom registered in a preload; there is no built-in jsdom environment.

Build and deploy

bun build ./src/index.ts --outdir dist --target bun --minify
bun build ./cli.ts --compile --outfile mycli          # standalone executable
FROM oven/bun:1 AS deps
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production

FROM oven/bun:1-slim
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER bun
CMD ["bun", "run", "src/index.ts"]

Pin the image to a specific Bun version for reproducible builds.

Node compatibility traps

  • bun run dev executes the script’s own binary; if that binary has a #!/usr/bin/env node shebang (Vite, Next, tsc), it runs on Node. Use bun --bun run dev to force Bun.
  • Bun loads .env, .env.<NODE_ENV> and .env.local automatically. A stale local .env can override what you expect in tests; Node does not do this.
  • Bun strips TypeScript types without checking them. Keep tsc --noEmit in CI.
  • Native addons using N-API generally work; addons tied to V8 or Node internals may not.
  • Check Bun’s Node.js compatibility page for each node: module the app uses beyond the common ones (vm, inspector, cluster, worker_threads options, http2).
  • process.versions.bun exists only under Bun; use it for the rare runtime branch.

Checklist

  • Scope agreed (package manager, tests, runtime)
  • bun.lock committed; old lockfile removed after CI passes
  • trustedDependencies lists packages that need install scripts
  • tsc --noEmit and bun test pass; critical paths also run once under Node if both are supported
  • Docker image pinned; graceful stop on SIGTERM

Gotchas

  • Bun.serve default idleTimeout is 10 s; long polling and SSE need a larger value or server.timeout(req, 0) per request.
  • bun:sqlite is synchronous and blocks the event loop on large queries.
  • bunx may use a cached package version; pin versions in CI (bunx [email protected]).

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Bun Runtime"
  short_description: "Adopt Bun without surprise Node fallbacks"
  default_prompt: "$bun-runtime Switch this Node project to bun install and bun test without breaking CI"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem