Bun v1.0.20

Jarred Sumner ยท December 24, 2023

Bun v1.0.20 reduces memory usage in fs.readlink, fs.readFile, fs.writeFile, fs.stat and HTMLRewriter. Fixes a regression where setTimeout caused high CPU usage on Linux. HTMLRewriter.transform now supports strings and ArrayBuffer. fs.writeFile() and fs.readFile() now support hex & base64 encodings. Bun.spawn shows how much CPU & memory the process used.

Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager โ€” all in one. In case you missed it, here are some of the recent changes to Bun:

  • v1.0.15 - Fixes 23 bugs (addressing 117 ๐Ÿ‘ reactions), tsc starts 2x faster. Stable WebSocket client, syntax-highlighted errors, cleaner stack traces, add custom test matchers with expect.extend() + additional expect matchers.
  • v1.0.16 - Fixes 49 bugs (addressing 38 ๐Ÿ‘ reactions). Concurrent IO for Bun.file & Bun.write gets 3x faster and now supports Google Cloud Run & Vercel, Bun.write auto-creates the parent directory if it doesn't exist, expect.extend inside of preload works, napi_create_object gets 2.5x faster, bugfix for module resolution impacting Astro v4 and p-limit, console.log bugfixes
  • v1.0.17 - Fixes 15 bugs (addressing 152 ๐Ÿ‘ reactions). bun install postinstall scripts run for top 500 packages, bunx supabase starts 30x faster than npx supabase, bunx esbuild starts 50x faster than npx esbuild and bugfixes to bun install
  • v1.0.18 - Fixes 27 bugs (addressing 28 ๐Ÿ‘ reactions). A hang impacting create-vite & create-next & stdin has been fixed. Lifecycle scripts reporting "node" or "node-gyp" not found has been fixed. expect().rejects works like Jest now, and more bug fixes
  • v1.0.19 - Fixes 26 bugs (addressing 92 ๐Ÿ‘ reactions). Use @types/bun instead of bun-types. Fixes --frozen-lockfile bug. bcrypt & argon2 packages now work. setTimeout & setInterval get 4x higher throughput. module mocks in bun:test resolve specifiers. Optimized spawnSync() for large stdio on Linux. Bun.peek() gets 90x faster, expect(map1).toEqual(map2) gets 100x faster. Bugfixes to NAPI, bun install, and Node.js compatibility improvements

To install Bun:

curl -fsSL https://bun.sh/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun

To upgrade Bun:

bun upgrade

Reduced memory usage in node:fs

This release makes infrastructure changes that reduce memory usage for async work involving strings throughout Bun.

We've also improved internal reporting of memory allocations in debug builds, which helps us identify memory leaks.

After reading a symlink 1,000,000 times in parallel

MethodJavaScript runtimePlatformRSS (MB)
fs.readlinkBun v1.0.20macOS arm6445 MB
fs.readlinkNode.js v21.4.0macOS arm6470 MB
fs.readlinkBun v1.0.19macOS arm64107 MB

fs.stat uses up to 2x less memory

After stat'ing a file 1,000,000 times in parallel

MethodJavaScript runtimePlatformRSS (MB)
fs.statBun v1.0.20 --smolmacOS arm6445 MB
fs.statBun v1.0.20macOS arm6471 MB
fs.statNode.js v21.4.0macOS arm6470 MB
fs.statBun v1.0.19macOS arm64107 MB

fs.writeFile memory leak fixed

Writing a 16 MB non-ascii string to disk 100 times

MethodJavaScript runtimePlatformRSS (MB)
fs.writeFileBun v1.0.20macOS arm6491 MB
fs.writeFileNode.js v21.4.0macOS arm64130 MB
fs.writeFileBun v1.0.19macOS arm641,696 MB

HTMLRewriter.transform now supports strings and ArrayBuffer

HTMLRewriter lets you transform HTML on the fly. It's useful for things like:

  • Rewriting URLs to point to a CDN
  • Extracting metadata from HTML
  • Getting all the images on a page

Bun's implementation of HTMLRewriter now supports passing strings and ArrayBuffers to HTMLRewriter.transform. Previously, you had to pass a Response object.

Passing a string to HTMLRewriter.transform will parse the string as HTML and return a string.

const html = new HTMLRewriter()
  .on("img", {
    element(element) {
      element.setAttribute("src", "https://example.com/image.png");
  .on("title", {
    element(element) {
      element.setInnerContent("Hello world!");
    "<html><body><img src='image.png'><title>My page</title></body></html>",

// <html><body><img src="https://example.com/image.png"><title>Hello world!</title></body></html>

Memory leak in HTMLRewriter fixed

A memory leak in HTMLRewriter has been fixed. This was caused by not calling the destructor at the appropriate time.

fs.readFile() and fs.writeFile() now support hex & base64 encodings

fs.readFile and fs.writeFile now support hex and base64 encodings. This is useful for reading and writing binary files.

import { readFile, writeFile } from "fs/promises";

const buffer = await readFile("image.png", { encoding: "hex" });
await writeFile("image-copy.png", buffer, { encoding: "hex" });

Fixed: regression where setTimeout caused high CPU usage on Linux

There was a regression in Bun v1.0.19 where setTimeout caused high CPU usage on Linux. This was fixed, thanks to @cirospaciari.

Bun.spawn now reports resourceUsage

Bun.spawn & Bun.spawnSync gets a resourceUsage method, which reports CPU & memory usage for the process.

import { spawnSync } from "bun";

// for spawnSync, it is a property on the return value
const { resourceUsage } = spawnSync([
  "console.log('Hello world!')",


// in Bun.spawn, it is a function that you call

This prints:

ResourceUsage {
  contextSwitches: {
    voluntary: 0,
    involuntary: 120,
  cpuTime: {
    user: 5578n,
    system: 4488n,
    total: 10066n,
  maxRSS: 22020096,
  messages: {
    sent: 0,
    received: 0,
  ops: {
    in: 0,
    out: 0,
  shmSize: 0,
  signalCount: 0,
  swapCount: 0,

Thanks to @cirospaciari for this contribution!

We used this to write tests to prevent the regression where setTimeout caused high CPU usage on Linux from happening again.

Infrastructure improvements

In debug builds of Bun, we've added support for the 'malloc zone' feature of macOS to track memory allocations in native code more accurately. This helps us identify memory leaks.

This prints output like the below:

  blocks_in_use:   278
  size_in_use:     2318192
  max_size_in_use: 2696288
  size_allocated:  19922944

WebKit Malloc:
  blocks_in_use:   0
  size_in_use:     0
  max_size_in_use: 0
  size_allocated:  0

  blocks_in_use:   2
  size_in_use:     240
  max_size_in_use: 16976
  size_allocated:  1048576

  blocks_in_use:   1
  size_in_use:     32
  max_size_in_use: 16832
  size_allocated:  1048576

We've also changed how we do cross-thread cloning of JavaScript strings. Previously, we sometimes cloned a string twice and then never used the first clone. This was extremely wasteful. Now, we only clone a string once and only if we definitely need to.

bun init now uses @types/bun

bun init now uses @types/bun instead of bun-types, and we've enabled some of the new options like verbatimModuleSyntax.

Thanks to @ArnaudBarre for this contribution!

Thanks to five contributors!

Full Changelog