Bun v0.5

Ashcon Partovi · January 18, 2023

We're hiring C/C++ and Zig engineers to build the future of JavaScript!

Bun v0.5 is packed with new features including npm workspaces, Bun.dns, and support for node:readline. There's improved compatibility with node:tls and node:net so several database drivers now work in Bun for the first time, including Postgres.js, mysql2, node-redis, and others. Bun also continues to get faster and more stable — Buffer instantiation is 10x faster, crypto.createHasher() is 50x faster, and bun install got dozens of bugfixes.

# Install Bun
curl https://bun.sh/install | bash

# Upgrade to latest release of Bun
bun upgrade

Workspaces in package.json

Bun now supports workspaces in package.json, and it's fast. Bun installs the Remix monorepo in about 500ms on Linux.

  • 28x faster than npm install
  • 12x faster than yarn install (v1)
  • 8x faster than pnpm install

What are workspaces?

Workspaces make it easy to develop complex software as a monorepo consisting of several independent packages. To try it, specify a list of sub-packages in the workspaces field of your package.json; it's conventional to place these sub-packages in a directory called packages.

  "name": "my-project",
  "version": "1.0.0",
  "workspaces": ["packages/a", "packages/b"]

Bun doesn't support globs for workspace names yet, but this is coming soon!

This has a couple major benefits.

  • Code can be split into logical parts. If one package relies on another, you can simply add it as a dependency with bun add. If package b depends on a, bun install will symlink your local packages/a directory into the node_modules folder of b, instead of trying to download it from the npm registry.
  • Dependencies can be de-duplicated. If a and b share a common dependency, it will be hoisted to the root node_modules directory. This reduces redundant disk usage and minimizes "dependency hell" issues associated with having multiple versions of a package installed simultaneously.

Bun.dns and node:dns

Bun can now resolve domain names using the built-in Bun.dns API. At the moment, Bun.dns exposes a single function: lookup.

import { dns } from "bun";

const records = await dns.lookup("example.com", { family: 4 });
console.log(records); // [{ address: "" }]

We've also added a minimal implementation of Node.js' node:dns that uses Bun.dns under the hood. It's powered by c-ares and non-blocking getaddrinfo on MacOS.

import { resolve4 } from "node:dns/promises";

const records = await resolve4("example.com");
console.log(records); // [ "" ]

Sockets using node:tls and node:net

Bun now supports the creation of sockets using net.connect() and tls.connect(). This unblocks several database driver libraries. A handful of representative examples:

Connect to Postgres in Bun using Postgres.js by @porsager:

import postgres from "postgres";

const sql = postgres();
const [{ version }] = await sql`SELECT version()`;

console.log(version); // "PostgreSQL 14.2 ..."

Connect to MySQL in Bun using mysql2 client by @sidorares:

import { createConnection } from "mysql2/promise";

const connection = await createConnection({
  host: "localhost",
  user: "root",
  database: "test",

const [rows] = await connection.execute("SELECT 1+2 AS count");
console.log(rows); // [{ count: 3 }]

Connect to Redis from Bun using the official Node.js client:

import { createClient } from "redis";

const client = createClient();
await client.connect();

await client.set("key", "Hello!");
const value = await client.get("key");

console.log(value); // "Hello!"

Support for node:readline

Bulding CLI tools should be much easier now that Bun supports the node:readline module.

import * as readline from "node:readline/promises";

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: true,

const answer = await rl.question("How fast is Bun from 1 to 10?\n");
if (parseInt(answer) > 10) {
  console.log("Good answer!");

Running this script yields:

bun readline.ts
How fast is Bun from 1 to 10?
Good answer!

Custom headers in WebSocket

A long-standing feature request on the WebSocket spec is the ability to set custom headers when opening a WebSocket. While this hasn't yet landed in the WebSocket standard, Bun now implements it. This allows users to customize the headers used for the WebSocket client handshake request.

const ws = new WebSocket("ws://localhost/chat", {
  headers: {
    Authorization: "...",

Improvements to bun wiptest

While bun wiptest is still a work in progress, we continue to increase Bun's compatibility with Jest.


You can use test.skip() to skip unwanted tests.

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

describe("fetch()", () => {
  test.skip("can connect to localhost", async () => {
    const response = await fetch("http://localhost");

  test("can connect to example.com", async () => {
    const response = await fetch("http://example.com");

When you skip a test, it will appear as grayed out in the test output.

bun wiptest output


You can use expect(fn).toThrow() to catch expected errors.

import { test, expect } from "bun:test";

test("catch error", async () => {
  expect(() => {
    throw new Error();

describe labels are included in the output

Previously, nested describe labels were not included in the test runner output. Thanks to @ethanburrell, this has been fixed.


✓ outer > my test


✓ outer > inner > my test

Test file:

import { describe, test } from "bun:test";

describe("outer", () => {
  describe("inner", () => {
    test("my test", () => {});

Performance boosts

10x faster new Buffer()

Previously, the Buffer implementation in Bun was using Object.setPrototypeOf() to create each new instance. Eliminating this bottleneck makes it 10x faster to instantiate a small Buffer in Bun.

10x faster Buffer

50x faster crypto.createHash()

Previously, Bun was using a pure JavaScript implementation of crypto.createHash(). Now it's implemented using native code from BoringSSL, yielding a 50x speed improvement.

Support for HTTPS_PROXY

Bun will now recognize the HTTPS_PROXY, HTTP_PROXY, and NO_PROXY enviroment variables when making outgoing HTTP requests, which includes fetch() and bun install. These variables allow you to specify a proxy to forward, or not forward, certain HTTP requests and are useful when running Bun within a corporate firewall.

export HTTPS_PROXY="http://proxy.example.com:8080"
export NO_PROXY="localhost,noproxy.example.com"

If you want to learn more about these variables, GitLab wrote a nice explainer.

Module resolution changes

There are two changes to module resolution that may impact a few packages.

  1. Bun no longer checks the browser property in package.json. This is because some packages would disable Node.js functionality, which is not what we want for Bun.
  2. For better Node.js & npm compatibility, Bun's JavaScript runtime now reads the "node" export condition in package.json exports.

The order Bun's JavaScript runtime reads package.json "exports" conditions is:

["bun", "worker", "module", "node", "browser", "default"];

This means that if a package has a "node" export condition, it will be used instead of the "default" or "browser" export condition.


While we continue to add new features to Bun, we're still focused on improving stability and fixing bugs. This release fixes a number of issues

Fixes to bun install

Several bugs with Bun's package manager are fixed in this release, mostly by @alexlamsl. Thanks Alex!

#1664Previously, scoped and private packages configured with bun install would have a registry of localhost, which made very little sense. We've fixed this and private registries will now default to the default registry if not specified, which is usually registry.npmjs.org
#1667Typically, npm clients are supposed to pass the npm-auth-type header, but bun install wasn't. We've fixed this and now bun install will pass the npm-auth-type header
a345efdIn some CI environments, like Vercel, linking node_modules/.bin would fail because the /proc filesystem (used to resolve absolute file paths) wasn't mounted. We've fixed this by falling back to fchdir and getcwd when /proc/fd is not available
385c81dA crash sometimes happened when running bun add <package> if the "dependencies" list went from empty to not empty in a package.json
#1665Use npm as default registry when scopes are configured
#1671Fix logging verbosity in bun install
#1799Fix lifecycle script execution in bun install

New APIs

6260aaaImplements crypto.scrypt() and more node:crypto APIs
d726a17Implements Bun.RIPEMD160
940ecd0Implements process.uptime() and process.umask()
85eda20Implements Bun.CryptoHasher
#1727Implements expect().toThrow()
#1659Implements Buffer.{swap16,swap32,swap64}@malcolmstill
c18165bAdds support for ttl: true in Bun.listen()
734b5b8Adds a closeActiveConnections parameter to Server.stop() and Socket.stop()
8e9af05dChanges WebSocket to accept URLs with http or https protocol

Additional fixes

#1670Fix incorrect 206 Partial Content response
#1674Fixes a bug where console.log({ name: "" }) would print incorrect formatting
3d60b87Fixes ReadableStream.pipeTo()
#1689Fixes bun wiptest not waiting for async lifecycle hooks
#1700Fixes lingering processes with Bun.listen() and Bun.connect()
#1705 #1730Fixes the connectError callback not being invoked with Bun.listen()
#1695Fixes a transpiler bug that affected @tensorflow/tfjs-backend-wasm — @theoparis
#1716Fixes TextDecoder being exported as TextEncoder (oops!) — @torbjorn-kvist
#1734Fixes unhandled Promise rejections not causing an error exit code
fadd1c0Fixes when Bun.connect() would not reject on connection error
2392e48Fixes when an uncaught error in a describe block would exit bun wiptest
88ffdc5Fixes a transpiler bug with export default class implements
#1800Fixes Response not accepting a status under 2xx
7dd28bbFixes an issue where Bun.which() would inadvertently detect a directory on macOS
Fix various bugs and improved compatibility with Node-API: 59655d0 f79301c 994e58b c505f17 02f0212 d54e23c 59655d0 f79301c


Thank you to everyone who contributed to Bun v0.5!