# Bun vs Node benchmark servers

These files are what the shipmunks.dev post [Bun vs Node](/blog/bun-vs-node/) timed.

- `http.mjs` is `node:http`. `GET /json` returns `{"message":"hello","ok":true}` with `content-type: application/json; charset=utf-8` and a content-length. No other headers are set in the file.
- `bun-serve.mjs` is `Bun.serve` with the same path, the same body, and the same two headers.
- `express.mjs` is Express on the same path. It calls `res.json` and leaves Express defaults on, including ETag and `X-Powered-By`.
- `suite.test.mjs` is the small `node:test` file. `node --test` and `bun test` both run this file.

The runner is `scripts/benchmarks/bun-vs-node/bench.mjs` in the shipmunks.dev repo. It expects Node.js 26.13.0 (the active LTS used for the published run) and Bun 1.4.4. It installs Express 5.3.0 and autocannon 8.0.0 under `/tmp/bun-vs-node-bench` (override with `BENCH_WORK`), copies the server files there, checks the JSON, then runs autocannon. The load generator is that Node process for every server, including the Bun servers.

Each server gets one 10 second warmup at 100 connections, then three measured runs of 10 seconds at 100 connections, pipelining 1. The servers run one at a time. The median in the post is the middle of the three measured `requests.average` values. The warmup is stored in the CSV and is not in the median. Peak RSS is `VmHWM` from `/proc/<pid>/status` after that server's warmup and measured runs.

The install timing is a fresh copy of this repo's `package.json` and `package-lock.json`, with the npm cache and the Bun cache cleared before every run. The command is `npm install` or `bun install`, three runs each. The test timing is this suite, one warmup then three measured runs, wall clock around the process.

A rerun will not match the medians. The rows from the run published with the post are [bun-vs-node-benchmark.csv](/files/bun-vs-node-benchmark.csv).
