Back to News & Insights
DevOps September 12, 2026 · 6 min read

Bun 1.4's Rust rewrite cuts script startup time from 10.5ms to 4.2ms

I measured Bun 1.4's Rust runtime against 1.3.14: startup dropped 60%, idle memory 46%, but idle CPU only fell 2.5x against a claimed 5x, and invoking Bun as a node symlink silently stops loading .env files.

Bun 1.4's Rust rewrite cuts script startup time from 10.5ms to 4.2ms

Bun 1.4 shipped on 20 August as the first stable release built on Bun's new Rust-based core, replacing the Zig runtime layer that shipped everything since 1.0. The release notes claim faster startup, less idle CPU, and lower memory. I installed the last pre-rewrite build and the new one side by side and measured all three, plus what happens when you point a node symlink at the new binary, which is how a lot of people actually run Bun in production.

I put Bun 1.3.14 in ~/.bun-old and 1.4.2 in ~/.bun-new using the official install script with a pinned version tag, so both binaries sit on the same machine and I can call either one directly.

My first pass measured bun --version twenty times per binary. The difference was almost nothing: 2.24ms median for 1.3.14, 2.00ms for 1.4.2. That command exits before it initialises the JS engine, so it wasn't measuring what the release notes were talking about. Once I switched to actually running a script, the gap showed up.

Thirty runs of bun run hello.js each, timed with time.perfcounter() around a subprocess.run call:

| | median | min | max | |---|---|---|---| | 1.3.14 | 10.54ms | 9.32ms | 18.00ms | | 1.4.2 | 4.22ms | 3.90ms | 5.25ms |

That's a 60% drop in median startup time, better than the "50% faster on Linux" the release notes claim. The old binary also had a much longer tail (up to 18ms), while the new one stayed inside a 1.4ms band across all 30 runs.

I ran a two-line Bun.serve echo server on each version, hit it once to warm it up, then read VmRSS from /proc//status after a further 60 seconds of doing nothing. Three runs each:

| | RSS after warmup | RSS after 60s idle | |---|---|---| | 1.3.14 (run 1) | 34,876 KB | 34,960 KB | | 1.3.14 (run 2) | 34,924 KB | 34,928 KB | | 1.3.14 (run 3) | 34,872 KB | 34,936 KB | | 1.4.2 (run 1) | 18,864 KB | 18,920 KB | | 1.4.2 (run 2) | 19,068 KB | 19,136 KB | | 1.4.2 (run 3) | 19,056 KB | 19,144 KB |

Averaging the idle figures: 34.9MB down to 19.0MB, a 46% reduction. The release notes say "up to 35%", so on this workload it beat its own number, though a bare echo server is close to the best case for a claim like this — there's no application memory to dwarf the runtime's own footprint.

The release notes claim 5x less idle CPU. This is the one I'd flag as not backed by what I measured.

I sampled utime + stime from /proc//stat before and after a 60-second idle window, three times per version (100 clock ticks per second, so each tick is 10ms of CPU time):

| run | 1.3.14 ticks/60s | 1.4.2 ticks/60s | |---|---|---| | 1 | 11 | 6 | | 2 | 11 | 4 | | 3 | 10 | 3 | | mean | 10.67 | 4.33 |

That's roughly 0.178% average CPU for 1.3.14 against 0.072% for 1.4.2 — a real improvement, and in the right direction, but a 2.5x reduction, not 5x. Both numbers are small enough that a single bad sample would swing the ratio a lot, which is exactly why I went from a 5-second window (0 to 2 ticks total, useless) to 60 seconds before trusting any of it. It's possible Bun's 5x figure comes from a different idle workload — something with an active file watcher or timer loop rather than a bare Bun.serve with no traffic — but on the simplest possible idle server, I got 2.5x.

The release notes don't claim a throughput number, so I measured one anyway, since idle numbers only tell you about servers doing nothing. I ran autocannon -c 50 -d 8 against the same echo server, three runs per version:

| run | 1.3.14 avg req/s | 1.4.2 avg req/s | |---|---|---| | 1 | 29,456 | 62,310 | | 2 | 29,578 | 61,998 | | 3 | 28,042 | 62,922 | | mean | 29,025 | 62,410 |

That's 2.15x more requests per second at 50 concurrent connections, and p50 latency dropped from 1ms to sub-millisecond in autocannon's output. This one held up better under concurrency than at idle, which is the opposite of what I expected going in — I assumed a rewrite aimed at idle efficiency would show its biggest gains with nothing happening, not under load.

Bun positions itself as a drop-in replacement for Node, and one common way people use that is symlinking a node binary to bun so existing tooling picks it up without changes. I tested exactly that setup.

That's 1.3.14, invoked through a symlink literally named node. It loads .env as expected. Now the same setup against 1.4.2:

Want to discuss this further?

Book a free strategy call with our team to see how these insights apply to your specific business goals.

Book a consultation