Trying Out Bun For Node.js Package Management

It's super fast! Sometimes.
A boy is shovelling bread into the baker's oven as one picks up sheaves of wheat and another holds up a tray. Engraving by Giovanni Volpato after Francesco Maggiotto
A boy is shovelling bread into the baker's oven as one picks up sheaves of wheat and another holds up a tray. Engraving by Giovanni Volpato after Francesco Maggiotto. Maggiotto, Francesco, 1750-1805. Public Domain Mark. Source: Wellcome Collection.

Bun is —to quote its homepage— “an all-in-one JavaScript runtime & toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager.” Quoting its homepage again, “You don’t need to use the Bun runtime to use Bun as a package manager.”

So I tried using it as a package manager. Install Bun once, run bun init in a fresh project, and then use bun add <package> to add packages, bun install to install package.json-specified dependencies, and bun run <script> to run scripts.

Dependency installation performance

The Bun team brags that it installs Node.js dependencies extremely quickly.

I was about to start building https://tailwindcss-fluid-font-size.olets.dev/. I’ll call it “Project 1”. It’s a VitePress 1.x site with Tailwind and my fluid typography Tailwind plugin, tailwindcss-fluid-font-size. It would have no tests and no bundling, but it would have dependencies.

I wanted to also see how Bun did with a dependency tree size more representative of the projects I work on. “Project 2” is an existing Eleventy 3 site with quite a few devDependencies. It uses pnpm for package management.

I compared bun install, npm install, and pnpm install, all after an initial <manager> install && rm -rf node_modules to take advantage of any global cache support and/or lockfile. This reflects my real life use — my projects mostly use similar stacks to each other, most dependencies I add will have been added to some other project before; and I commit my package manager lockfiles, so they’re in place when I run the dependency installation command.

I did not test with Yarn. tailwindcss-fluid-font-size is published on JSR, and installing it seems to require Yarn not-Classic. On my test machine I’ve only used Yarn Classic, and I didn’t want to risk screwing up my environment.

Times are the best out of five runs, as measured with Python’s timeit.

Package manager Install duration (Project 1) Install duration (Project 2)
bun 850 ms 🥇 13.4 s 🐌
pnpm 2.46 s 🥈 4.56 s 🥈
npm 3.65 s 🥉 3.91 s 🥇

Bun’s homepage highlights a benchmark where bun install is 17 times as fast as pnpm install and 29 times as fast as npm install. (And pnpm’s Benchmarks page highlights a benchmark where pnpm install is 1.5 times as fast as npm install.)

On Project 1, the VitePress site, I clocked bun install as close to 3 times as fast as pnpm install, and over 4 times as fast as npm install.

On Project 2, the Eleventy site, I clocked bun install at not quite 1/3 as fast as pnpm install 😳 Notably, based on the console output during installation, Bun may have struggled to install sharp, the image processor used by eleventy-image. I haven’t investigated further.

CI

There was a time when using a package manager other than npm with CI-as-a-service and continuous deployment-as-a-service was a pain. Happily the services I use now support both pnpm and Yarn. Will they choke on about Bun?

Project 1 lives on Codeberg, which uses Forjego Actions for CI. Forjego Actions can use GitHub Actions actions. Bun has an official GitHub Actions setup action oven-sh/setup-bun, similar to actions/setup-node (for npm and Yarn) and pnpm/action-setup (for pnpm). So using Bun in my CI workflow was straight-forward.

Both sites are deployed on Vercel. Vercel’s Bun support just works™.

Takeaway

On one of the two projects I tested, bun install was faster enough than both npm install and pnpm install to feel faster.

On the other project, bun install took so much longer than npm install or pnpm install that I ran all three tests several times, thinking there must have been an outside anomaly in CPU availability, Wi-Fi strength, or something. But no, I reproduced it every time. (The reported results are those from the first run.)

Back around 2017 the team I was on switched from npm to Yarn (Classic) for Node.js package management, primarily for the faster installs. A few years ago I moved over to pnpm, again primarily for the faster installs. Based on this initial experience, Bun is the fastest yet in some contexts. It’s worth trying, if the services you use support it.

Tip

With package.json’s packageManager field, you can specify a project’s package manager. That makes it more reasonable to use different package managers on different projects. In some cases you’ll get an error if you try to use a different manager than the one specified, and in all cases it’s convenient documentation.

Articles You Might Enjoy

Or Go To All Articles