Benchmark Report spec verified pre-load0 errors / 12,231,113 requestsbombardier --fasthttp · best of 3 rounds

Every server implements the exact same spec - GET / (Ping), GET /id/1?name=bun (Query), POST /json (Body), and GET /video (Video) - and is byte-checked against the expected responses before a single request is fired. Load is generated with bombardier --fasthttp against build artifacts, not dev servers.

AMD EPYC 9V74 80-Core Processor4 cores · 16GB RAMLinux 6.17.0-1022-azureBun 1.4.0 / v20.20.2Thursday, October 8, 2026

Mean score · buntok

82,651req/s

#1 of 5 · +0.8% vs elysia

Correctness

0errors

12,231,113 requests across 5 frameworks × 4 routes

Cold start · buntok

51ms

rank 1/5 · fastest: buntok 51 ms

RSS growth · buntok

+11MB

41.3 MB → 52.2 MB under sustained load

01

Final Scoreboard

score = mean of per-route req/s · green = best in column · lower is better for everything except score
#FrameworkRuntimeScore (req/s)P50 msP99 msCold startBundleRSS Δ
1buntokbun82,6515.230.351 ms828.9 KB+11 MB
2elysiabun82,0235.331.251 ms375.2 KB+5 MB
3honobun71,1855.630.056 ms19.6 KB+10 MB
4fastifybun50,0488.2108.4156 ms584.9 KB+98 MB
5expressbun20,72519.9109.3106 ms805.4 KB+94 MB

P50 = median across routes · P99 = worst route · cold start = spawn → first successful GET / · RSS Δ measured after 2 × 10 s of load · bundle = minified Bun.build artifact.

02

Head-to-Head by Route

bars scaled to the route winner · W = warmup round (discarded) · P50/P99 in ms
/Ping
buntok

static 2-byte body - routing + write path

buntok140,675req/s
W 140.2k140.5k140.7kP50 3.5 · P99 5.0 ms1,402,540 req0 err
elysia139,588req/s
W 139.7k139.6k139.5kP50 3.5 · P99 5.3 ms1,390,930 req0 err
hono108,652req/s
W 106.4k106.8k108.7kP50 4.5 · P99 6.1 ms1,082,106 req0 err
fastify77,704req/s
W 77.2k76.2k77.7kP50 6.1 · P99 11.4 ms773,854 req0 err
express32,359req/s
W 30.6k32.4k31.7kP50 15.0 · P99 25.6 ms322,998 req0 err
4,972,428 requests total0 errors10s × 2 rounds @ 500 conns
/id/1?name=bunQuery
elysia

dynamic params + query parsing, must echo "1 bun"

elysia101,028req/s
W 99.5k100.0k101.0kP50 4.9 · P99 6.1 ms1,007,759 req0 err
buntok99,469req/s
W 97.2k99.5k98.9kP50 5.0 · P99 6.2 ms991,786 req0 err
hono93,294req/s
W 92.8k92.8k93.3kP50 5.3 · P99 7.4 ms930,680 req0 err
fastify72,825req/s
W 74.9k72.0k72.8kP50 6.7 · P99 10.0 ms726,582 req0 err
express30,314req/s
W 29.1k30.3k29.7kP50 16.3 · P99 23.6 ms302,915 req0 err
3,959,722 requests total0 errors10s × 2 rounds @ 500 conns
POST /jsonBody
buntok

JSON parse + mirror of {"hello":"world"}

buntok89,694req/s
W 87.7k89.7k89.7kP50 5.5 · P99 6.7 ms894,782 req0 err
elysia86,701req/s
W 83.6k85.1k86.7kP50 5.7 · P99 7.0 ms864,928 req0 err
hono82,029req/s
W 80.4k80.9k82.0kP50 6.0 · P99 6.9 ms818,583 req0 err
fastify49,557req/s
W 50.1k49.6k46.5kP50 9.7 · P99 18.3 ms494,229 req0 err
express20,126req/s
W 19.1k20.1k18.9kP50 23.6 · P99 40.7 ms201,226 req0 err
3,273,748 requests total0 errors10s × 2 rounds @ 500 conns
/videoVideo
elysia

14.1 MB mp4 stream at 10 connections, no buffering

elysia773req/s
W 788773772P50 12.2 · P99 31.2 ms7,753 req0 err
buntok767req/s
W 773767767P50 12.2 · P99 30.3 ms7,690 req0 err
hono766req/s
W 753766764P50 12.3 · P99 30.0 ms7,695 req0 err
fastify105req/s
W 105102105P50 94.2 · P99 108.4 ms1,057 req0 err
express101req/s
W 10410199P50 98.0 · P99 109.3 ms1,020 req0 err
25,215 requests total0 errors10s × 2 rounds @ 500 conns
03

Latency Distribution

milliseconds · lower is better · grouped by route
FrameworkP50P90P95P99Max
/ - Ping
buntok3.53.63.85.054.0
elysia3.53.73.85.363.5
hono4.54.74.96.173.3
fastify6.16.87.111.4116.1
express15.015.617.325.6107.2
/id/1?name=bun - Query
buntok5.05.25.36.255.2
elysia4.95.15.26.149.9
hono5.35.55.87.448.8
fastify6.77.57.710.045.4
express16.316.917.323.651.5
POST /json - Body
buntok5.55.75.86.746.7
elysia5.75.96.07.044.8
hono6.06.36.46.946.0
fastify9.711.112.518.382.1
express23.627.834.240.799.2
/video - Video
buntok12.219.023.530.353.4
elysia12.218.823.331.241.1
hono12.319.223.130.063.5
fastify94.299.6101.5108.4121.1
express98.0102.3104.4109.3119.8
04

Resource Footprint

measured on the built artifact under the same load

Cold Start

lower = better

spawn → first successful GET /

buntok51 ms
elysia51 ms
hono56 ms
express106 ms
fastify156 ms

Bundle Size

lower = better

minified Bun.build artifact on disk

hono19.6 KB
elysia375.2 KB
fastify584.9 KB
express805.4 KB
buntok828.9 KB

Memory (RSS)

lower growth = better

idle → after 2 × 10 s of sustained load

elysia42.4 MB → 47.9 MB (+5MB)
hono34.2 MB → 43.7 MB (+10MB)
buntok41.3 MB → 52.2 MB (+11MB)
express58.6 MB → 152.2 MB (+94MB)
fastify69.7 MB → 167.9 MB (+98MB)
05

Trends & Comparisons

interactive charts - hover for exact values
06

Methodology

everything a skeptical reader needs to reproduce this
Routes
Ping (GET /), Query (GET /id/1?name=bun), Body (POST /json mirror), Video (GET /video, 14.1 MB mp4 stream)
Client
bombardier --fasthttp, 10s per route, 500 connections (Video: 10)
Rounds
3 total - 1 warmup (discarded) + 2 measured; best-of measured rounds per route
Framework order
rotated every round (alternating fairness)
Scoring
mean of per-route req/s
Latency
P50 = median across routes, P99 = worst route
Runtimes
express: bun, fastify: bun, hono: bun, elysia: bun, buntok: bun
Build
Bun.build (minify); servers run the built artifact
Core pinning
taskset: server cores 0-1, load generator cores 2-3
Route table
224 registered but never requested (real-world route table size)
Verification
4-route spec + dynamic-query edge asserted before the load test
Environment
Bun 1.4.0 / v20.20.2

The four-route spec matches bun-http-framework-benchmark so results are directly comparable there; the alternating rounds + best-of protocol comes from this project's A/B methodology for lower run-to-run noise.

Philosophy

What is Buntok?

Buntok is a decorator-first, zero-config API framework for Bun. Applications are built as class-based controllers whose routes and middleware are declared with Stage 3 decorators - @Controller, @Get, @Post, @Use - that run without the experimentalDecorators flag. Constructor dependencies are resolved by an IoC container with circular dependency detection, so services are assembled once at boot and injected where they are used.

Performance is a build-time concern rather than a tuning one: the middleware pipeline is compiled once at boot (AOT), static routes resolve in constant time, and dynamic segments match through a trie with an optional native Zig resolver over FFI. TypeScript support runs from route handler to validator - Zod validators via zValidator cover body, query, and params - and middleware such as CORS, compression, rate limiting, and request IDs ships in the core package rather than as a plugin ecosystem.

About These Results

These benchmarks were run on a AMD EPYC 9V74 80-Core Processor with 16GB RAM - a modest 4-core laptop, not a beefy CI runner, so the numbers are reproducible on real developer hardware. Every framework implements the same four spec routes and is verified against the expected responses before any load is applied. Load is generated with bombardier in fasthttp mode (500 connections, 10 s per route; Video uses 10). Frameworks run in alternating rounds - one warmup pass is discarded and the best of the measured rounds wins per route. Scores shown are exact means of the four route results, and the raw per-round numbers are printed for every route so outliers are visible, not hidden. Buntok is still in active development - the AOT router and middleware pipeline are being optimized. Results may vary across different hardware and configurations.

Source code: benchmarks/runner.ts