Skip to content

Dox vs Node.js: memory and speed

In our published benchmark on 2 CPU cores, a Dox app served more requests per second than a Node.js app and used much less memory.

This post compares Dox with Node.js only. The full tables also have other servers. You can find them, and the full method, on the Performance page. Every number here comes from that page.

How it was measured

  • Docker on an Apple M2, with 8 CPUs in the Docker VM (linux/arm64).
  • The server gets 2 CPUs. The load tool (wrk 4.1) runs on other CPUs.
  • One server runs at a time. Connections use keep-alive.
  • Each server has one worker per core. Dox has one isolate per core. Node has one process per core (node:cluster).
  • Dox is an AOT build (dox build) with Dart 3.13.6. Node is version 26.12.
  • 5 seconds of warm-up, then 15 seconds per route.
  • Peak memory is the peak of the whole container. It counts every process in the container. For Node, that includes the cluster primary process, which does not serve requests.

Plain HTTP

The test follows the rules of the-benchmarker/web-frameworks. Dox is a full Dox app with three routes. Node uses node:http.

Requests per second on 2 cores

GET /, 256 connections. Higher is better.

req/s
Doxfull app 638,067fastest
Nodenode:http 286,8472.2× slower

Dox · Node 26 · Dart 3.13 · 2 cores · 256 connections · wrk

Peak memory on 2 cores

GET /, 256 connections. Peak memory of the container, lower is better.

peak
Doxfull app 19 MBlowest
Nodenode:http 200 MB10.5× more

Dox · Node 26 · Dart 3.13 · 2 cores · 256 connections · wrk

GET /, requests per second:

Server 64 connections 256 connections 512 connections p99 at 256 peak MB
Dox 494,223 638,067 612,032 0.74 ms 19
Node (node:http) 252,866 286,847 296,257 1.70 ms 200

The other two routes, at 256 connections:

Server GET /user/:id POST /user
Dox 572,658 545,986
Node 274,067 301,084

At 256 connections, Dox served about 2.2 times more requests than Node on GET /. Its peak memory was 19 MB. Node's peak was 200 MB.

A small app with a database

This test is closer to a real app:

  • GET /json returns a map as JSON.
  • GET /db reads one row by id.
  • GET /queries?n=20 reads 20 rows, one after another.

Each app uses its database driver directly, with no ORM. Dox uses dox_postgres_native or dox_mysql_native. Node uses pg or mysql2. The server gets 2 CPUs and the database gets 2 CPUs. Each server has 4 database connections per core, so 8 in total. There was one run of 15 seconds per route.

Read one row on 2 cores

GET /db on Postgres 18, 256 connections. Higher is better.

req/s
Doxdox_postgres_native 266,432fastest
Nodepg 75,4473.5× slower

Dox · Node 26 · Dart 3.13 · Postgres 18 · 2 cores · 256 connections · wrk

Postgres 18, requests per second at 256 connections:

Server /json /db /queries?n=20 peak MB
Dox (dox_postgres_native) 555,216 266,432 17,772 106
Node (node:http + pg) 205,774 75,447 7,458 232

MySQL 8.4, requests per second at 256 connections:

Server /json /db /queries?n=20 peak MB
Dox (dox_mysql_native) 558,886 247,242 12,347 70
Node (node:http + mysql2) 205,630 74,809 6,001 239

On /db with Postgres, the p99 was 1.8 ms for Dox and 4.1 ms for Node.

Two things to see here:

  • Dox uses more memory with a database than without one: 106 MB with Postgres and 70 MB with MySQL, against 19 MB for plain HTTP.
  • Node also uses more memory, and stays at or above 200 MB in all three tests.

Why Dox uses less memory

We did not measure where each megabyte goes. But these design choices make a difference:

  • One process. Dox runs one process with one isolate per core. The isolates share one compiled program. The Node app runs one process per core plus a primary process, and each one loads its own JavaScript engine and its own copy of the app.
  • No JIT compiler. dox build makes an AOT binary, so no compiler runs next to your app. Node compiles JavaScript while the app runs.
  • Socket work outside the Dart heap. The native HTTP server reads the sockets and parses HTTP in native code.
  • One database pool. All isolates share one pool for the whole process.

What makes Dox fast explains each of these in more detail.

Measure your own app

These numbers come from one machine and very small apps. Your app is different. It has more routes, more data and its own code. A real app spends most of its time on the database, JSON and your own logic. So the gap in your app can be smaller or larger.

Before you choose, build your app with dox build and measure it on your own server. The Performance page shows how we ran the tests, so you can run them the same way.