What Our Benchmarks Actually Mean
Performance claims without methodology are marketing — and an absolute throughput number without the machine it ran on is worse, because it sounds precise while telling you nothing about your hardware. So here's how we measured bext against Next.js, and why every figure below is a ratio rather than a number we put on a billboard.
The Setup
Both systems run on the same machine — a Hetzner AX41-NVMe (AMD Ryzen 5 3600, NVMe SSD) on Ubuntu 22.04 — with the load generator (wrk21) on a separate box in the same datacenter over a private network. That "same machine" part is the whole point: the raw request rate is a property of the hardware, so it's meaningless to quote on its own. What carries over to your box is how bext compares to Next.js under identical conditions — so that's all we report.
We test a real Next.js 15 application with the App Router — not a "hello world" endpoint. The app has a dozen routes, dynamic segments, server components, and ISR pages. It's representative of a production SaaS dashboard.
The Comparison
Every figure is bext relative to Next.js standalone on the same machine. We've deliberately left the raw req/s and millisecond counts out — they describe our test box, not your production.
| Metric | bext vs Next.js standalone |
|---|---|
| Throughput — ISR cache hits | ~55× |
| Throughput — SSR, no cache | ~5.4× |
| p99 latency — cached | ~30× lower |
| p99 latency — SSR | ~23× lower |
| Memory — idle | ~4.3× less |
| Cold start | ~20× faster |
The throughput advantage, to scale:
Both bars are bext ÷ Next.js on the same box. The cache-hit advantage dwarfs the SSR one — which turns out to be the most important thing these numbers have to say.
What Drives the Difference
Cached responses (55x faster): bext serves ISR cache hits from an in-memory store in Rust. The hot path is a hash-table lookup and a memcpy — no JavaScript runs at all. Next.js reads from the filesystem, parses headers, and goes back through Node.js's event loop. The overhead is fundamentally different.
flowchart TB
subgraph B["bext — cache hit"]
direction LR
b1["Hash-table lookup"] --> b2["memcpy"] --> b3["Stream"]
end
subgraph N["Next.js — cache hit"]
direction LR
n1["FS read"] --> n2["Parse headers"] --> n3["Event loop"] --> n4["Stream"]
end
classDef fast fill:#fff1f2,stroke:#f43f5e,color:#0c0c0c,stroke-width:1.5px
class b1,b2,b3 fastSSR responses (5.4x faster): The gap is smaller here because both systems execute the same thing — JavaScript on V8. bext doesn't use a faster engine; it uses a leaner host. Next.js runs V8 inside Node.js, with the event loop, module graph, and middleware chain that come with it. bext embeds V8 directly in the Rust process and hands it nothing but your component tree. Same engine, far less around it.
Memory (4.3x less): Two things. First, bext's Rust core is lean — an idle instance's footprint is a fraction of a Node.js process's before any rendering happens. Second, and more important at scale: rendering happens in one shared V8 worker pool (8 workers by default) that's reused across every app the server hosts. Next.js gives each app its own Node.js process and its own V8 heap. So as you add apps, Next.js's memory grows roughly linearly while bext's barely moves — the render pool is already paid for. Run ten apps on one box and that difference is the whole ballgame.
Cold start (20x faster): bext starts fast because there's no JIT warmup of an application server, no module resolution, no TypeScript compilation on boot — the binary is pre-compiled and the render pool warms its isolates from a prebuilt V8 snapshot. Next.js has to boot Node.js, resolve modules, compile routes, and warm the JIT before it serves the first request.
What These Numbers Don't Tell You
Throughput is a ceiling, not a promise. Everything below is the part of performance bext can't fix for you.
Benchmarks measure throughput under controlled conditions. Real-world performance depends on:
- Database queries. If your page waits on a slow Postgres query, the runtime's render time is noise. Optimize your queries first.
- Third-party APIs. External calls add latency no runtime can eliminate. Use ISR caching so you don't hit them on every request.
- Asset size. bext serves static assets straight from memory with Brotli, but a multi-megabyte JavaScript bundle is slow no matter who ships it.
bext makes the server fast. You still have to make the application fast.
A Note on the Cache-Hit Ratio
The 55x figure is the one people quote, and it's also the one to be most careful with. It's measuring how fast bext can serve a response that's already rendered versus how fast Next.js standalone can — and almost all of that gap is the cost of not running JavaScript on a hit. It's a real and useful ratio (most production traffic is cache hits), but it's a caching benchmark, not a rendering one. The 5.4x SSR figure is the more honest measure of the runtime itself.
Reproducing the Benchmarks
The benchmark app, the wrk2 scripts, and the analysis tooling live in the repo under benchmarks/. Run them yourself:
bext run benchmarks/nextjs-app
wrk2 -t8 -c256 -d30s -R60000 http://localhost:3000/We publish results for every release at bext.dev/benchmarks. If you find a configuration where bext is slower than you'd expect, file an issue — we treat performance regressions as bugs.