Alpha

A secure, standards-based server runtime.

V8-based ECMAScript runtime for the server. WinterTC-compliant, capability secured.

import { serve } from "runtime:http";

const server = serve({ port: 8080 }, (req) => {
  return new Response("👋 Hello from ESRun!");
});

const { hostname, port } = await server.addr;
console.log(`listening on http://${hostname}:${port}`);

Benchmarks

Cold start (near-empty script)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
4.2ms
12MB
Deno
13.1ms
29MB
esrun
8.9ms
25MB
LLRT
3.6ms
11MB
Node.js
21.3ms
41MB
Parse + run ~100 KB script
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
14.6ms
16MB
Deno
21.7ms
30MB
esrun
20.5ms
26MB
LLRT
12ms
11MB
Node.js
34.6ms
44MB
Load a 300-module graph
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.3ms
24MB
Deno
32.5ms
33MB
esrun
34ms
27MB
LLRT
15.6ms
11MB
Node.js
99.1ms
61MB
Peak resident memory (idle)
Runtime
Perf (lower is better)
Bun
12MB
Deno
29MB
esrun
25MB
LLRT
11MB
Node.js
41MB
Peak resident memory (under load)
Runtime
Perf (lower is better)
Bun
135MB
Deno
123MB
esrun
107MB
LLRT
157MB
Node.js
136MB
Tight compute loop
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
115.2ms
22MB
Deno
238.3ms
38MB
esrun
197.7ms
35MB
LLRT
1899.4ms
11MB
Node.js
197.2ms
52MB
JSON parse/stringify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
155ms
36MB
Deno
218.3ms
54MB
esrun
201.9ms
51MB
LLRT
698.5ms
11MB
Node.js
276.1ms
78MB
JSON (large documents)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
366.3ms
120MB
Deno
524.4ms
137MB
esrun
486.2ms
91MB
LLRT
1651.3ms
130MB
Node.js
706.6ms
156MB
Regex (match, validate, replace)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
13.1ms
32MB
Deno
63.2ms
39MB
esrun
59.5ms
37MB
LLRT
979.1ms
11MB
Node.js
66ms
54MB
String building and slicing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
60ms
41MB
Deno
61.8ms
46MB
esrun
59ms
44MB
LLRT
168.5ms
11MB
Node.js
62.1ms
63MB
structuredClone
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
246.4ms
29MB
Deno
268.5ms
42MB
esrun
288.7ms
50MB
LLRT
421.1ms
11MB
Node.js
218.5ms
53MB
throw/catch + stack capture
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
349.4ms
41MB
Deno
4383.9ms
63MB
esrun
387.6ms
45MB
LLRT
386.1ms
11MB
Node.js
1485.2ms
62MB
async/await throughput
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
40.6ms
31MB
Deno
32.1ms
38MB
esrun
31.3ms
37MB
LLRT
581.8ms
11MB
Node.js
58.6ms
53MB
setTimeout churn
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.4ms
45MB
Deno
248.1ms
168MB
esrun
115.7ms
86MB
LLRT
47.6ms
44MB
Node.js
51ms
85MB
URL parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.6ms
46MB
Deno
104.2ms
46MB
esrun
93.3ms
44MB
LLRT
124.1ms
11MB
Node.js
35.3ms
63MB
TextEncoder/TextDecoder (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.7ms
35MB
Deno
70.2ms
49MB
esrun
66.7ms
47MB
LLRT
69.3ms
11MB
Node.js
69.3ms
63MB
TextEncoder/TextDecoder (64 KiB)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
68.2ms
36MB
Deno
150.8ms
39MB
esrun
104.5ms
38MB
LLRT
258.4ms
11MB
Node.js
317ms
53MB
base64 (atob/btoa)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
11.2ms
32MB
Deno
7.4ms
41MB
esrun
23.2ms
36MB
LLRT
33.6ms
11MB
Node.js
7ms
53MB
TypedArray / DataView
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
16.8ms
32MB
Deno
13.4ms
40MB
esrun
13.3ms
37MB
LLRT
72.5ms
11MB
Node.js
13.7ms
54MB
Headers / Request parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
226.5ms
35MB
Deno
1610.9ms
126MB
esrun
450.2ms
48MB
LLRT
—
—
Node.js
444ms
74MB
Multipart FormData round trip
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
20.4ms
43MB
Deno
399.8ms
110MB
esrun
95.5ms
42MB
LLRT
4705.5ms
11MB
Node.js
288.6ms
120MB
Date + Intl formatting
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
82ms
37MB
Deno
142.7ms
45MB
esrun
139.6ms
43MB
LLRT
—
—
Node.js
124.8ms
61MB
ReadableStream piping
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
5.7ms
25MB
Deno
10.6ms
44MB
esrun
9.7ms
42MB
LLRT
96.5ms
11MB
Node.js
11.6ms
60MB
SubtleCrypto SHA-256
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
421.8ms
35MB
Deno
435.6ms
41MB
esrun
346.1ms
34MB
LLRT
338.5ms
11MB
Node.js
547.4ms
57MB
HMAC + AES-GCM
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
86.2ms
31MB
Deno
93.5ms
41MB
esrun
36.3ms
35MB
LLRT
35.5ms
11MB
Node.js
203.9ms
61MB
ECDSA P-256 sign + verify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
208ms
24MB
Deno
2212.3ms
41MB
esrun
1181.3ms
34MB
LLRT
1066.1ms
11MB
Node.js
381.3ms
57MB
PBKDF2 (10k iterations)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
63.1ms
19MB
Deno
72ms
33MB
esrun
101.7ms
26MB
LLRT
99.2ms
11MB
Node.js
73.2ms
46MB
fetch (local server)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.9ms
21MB
Deno
36ms
42MB
esrun
43.3ms
39MB
LLRT
20.4ms
11MB
Node.js
100ms
89MB
File read (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
36.9ms
32MB
Deno
39.6ms
41MB
esrun
45.8ms
37MB
LLRT
27.9ms
11MB
Node.js
114.5ms
54MB
File read (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.3ms
38MB
Deno
68.7ms
166MB
esrun
28.3ms
92MB
LLRT
14.1ms
15MB
Node.js
60.1ms
112MB
File write (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.6ms
22MB
Deno
93.6ms
40MB
esrun
90.1ms
32MB
LLRT
97.7ms
11MB
Node.js
155.1ms
55MB
File write (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
21.5ms
24MB
Deno
46.1ms
80MB
esrun
39.6ms
33MB
LLRT
53.1ms
14MB
Node.js
59.2ms
91MB
File append (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.4ms
24MB
Deno
41.9ms
41MB
esrun
38.7ms
32MB
LLRT
—
—
Node.js
93.6ms
55MB
File append (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7ms
20MB
Deno
15.8ms
50MB
esrun
11.1ms
28MB
LLRT
—
—
Node.js
23.7ms
60MB
File stat (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
49.5ms
24MB
Deno
94.9ms
39MB
esrun
56.3ms
33MB
LLRT
41.3ms
11MB
Node.js
66.8ms
49MB
File stat (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
200ms
32MB
Deno
365.9ms
44MB
esrun
251.1ms
39MB
LLRT
161.7ms
11MB
Node.js
269.3ms
59MB
File exists (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.7ms
25MB
Deno
94.3ms
40MB
esrun
42.6ms
32MB
LLRT
46.9ms
11MB
Node.js
61.8ms
48MB
File exists (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
32.5ms
36MB
Deno
385.5ms
44MB
esrun
198.9ms
34MB
LLRT
194.8ms
11MB
Node.js
242.5ms
60MB
Glob scan
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
27ms
30MB
Deno
—
—
esrun
47.8ms
35MB
LLRT
—
—
Node.js
194.7ms
63MB
Spawn a child process
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
98.3ms
24MB
Deno
108.5ms
50MB
esrun
84.2ms
34MB
LLRT
94ms
11MB
Node.js
208.1ms
53MB
Cold start (near-empty script)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
4.2ms
12MB
Deno
13.1ms
29MB
esrun
8.9ms
25MB
LLRT
3.6ms
11MB
Node.js
21.3ms
41MB
Parse + run ~100 KB script
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
14.6ms
16MB
Deno
21.7ms
30MB
esrun
20.5ms
26MB
LLRT
12ms
11MB
Node.js
34.6ms
44MB
Load a 300-module graph
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.3ms
24MB
Deno
32.5ms
33MB
esrun
34ms
27MB
LLRT
15.6ms
11MB
Node.js
99.1ms
61MB
Peak resident memory (idle)
Runtime
Perf (lower is better)
Bun
12MB
Deno
29MB
esrun
25MB
LLRT
11MB
Node.js
41MB
Peak resident memory (under load)
Runtime
Perf (lower is better)
Bun
135MB
Deno
123MB
esrun
107MB
LLRT
157MB
Node.js
136MB
Tight compute loop
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
115.2ms
22MB
Deno
238.3ms
38MB
esrun
197.7ms
35MB
LLRT
1899.4ms
11MB
Node.js
197.2ms
52MB
JSON parse/stringify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
155ms
36MB
Deno
218.3ms
54MB
esrun
201.9ms
51MB
LLRT
698.5ms
11MB
Node.js
276.1ms
78MB
JSON (large documents)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
366.3ms
120MB
Deno
524.4ms
137MB
esrun
486.2ms
91MB
LLRT
1651.3ms
130MB
Node.js
706.6ms
156MB
Regex (match, validate, replace)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
13.1ms
32MB
Deno
63.2ms
39MB
esrun
59.5ms
37MB
LLRT
979.1ms
11MB
Node.js
66ms
54MB
String building and slicing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
60ms
41MB
Deno
61.8ms
46MB
esrun
59ms
44MB
LLRT
168.5ms
11MB
Node.js
62.1ms
63MB
structuredClone
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
246.4ms
29MB
Deno
268.5ms
42MB
esrun
288.7ms
50MB
LLRT
421.1ms
11MB
Node.js
218.5ms
53MB
throw/catch + stack capture
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
349.4ms
41MB
Deno
4383.9ms
63MB
esrun
387.6ms
45MB
LLRT
386.1ms
11MB
Node.js
1485.2ms
62MB
async/await throughput
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
40.6ms
31MB
Deno
32.1ms
38MB
esrun
31.3ms
37MB
LLRT
581.8ms
11MB
Node.js
58.6ms
53MB
setTimeout churn
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.4ms
45MB
Deno
248.1ms
168MB
esrun
115.7ms
86MB
LLRT
47.6ms
44MB
Node.js
51ms
85MB
URL parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.6ms
46MB
Deno
104.2ms
46MB
esrun
93.3ms
44MB
LLRT
124.1ms
11MB
Node.js
35.3ms
63MB
TextEncoder/TextDecoder (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.7ms
35MB
Deno
70.2ms
49MB
esrun
66.7ms
47MB
LLRT
69.3ms
11MB
Node.js
69.3ms
63MB
TextEncoder/TextDecoder (64 KiB)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
68.2ms
36MB
Deno
150.8ms
39MB
esrun
104.5ms
38MB
LLRT
258.4ms
11MB
Node.js
317ms
53MB
base64 (atob/btoa)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
11.2ms
32MB
Deno
7.4ms
41MB
esrun
23.2ms
36MB
LLRT
33.6ms
11MB
Node.js
7ms
53MB
TypedArray / DataView
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
16.8ms
32MB
Deno
13.4ms
40MB
esrun
13.3ms
37MB
LLRT
72.5ms
11MB
Node.js
13.7ms
54MB
Headers / Request parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
226.5ms
35MB
Deno
1610.9ms
126MB
esrun
450.2ms
48MB
LLRT
—
—
Node.js
444ms
74MB
Multipart FormData round trip
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
20.4ms
43MB
Deno
399.8ms
110MB
esrun
95.5ms
42MB
LLRT
4705.5ms
11MB
Node.js
288.6ms
120MB
Date + Intl formatting
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
82ms
37MB
Deno
142.7ms
45MB
esrun
139.6ms
43MB
LLRT
—
—
Node.js
124.8ms
61MB
ReadableStream piping
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
5.7ms
25MB
Deno
10.6ms
44MB
esrun
9.7ms
42MB
LLRT
96.5ms
11MB
Node.js
11.6ms
60MB
SubtleCrypto SHA-256
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
421.8ms
35MB
Deno
435.6ms
41MB
esrun
346.1ms
34MB
LLRT
338.5ms
11MB
Node.js
547.4ms
57MB
HMAC + AES-GCM
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
86.2ms
31MB
Deno
93.5ms
41MB
esrun
36.3ms
35MB
LLRT
35.5ms
11MB
Node.js
203.9ms
61MB
ECDSA P-256 sign + verify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
208ms
24MB
Deno
2212.3ms
41MB
esrun
1181.3ms
34MB
LLRT
1066.1ms
11MB
Node.js
381.3ms
57MB
PBKDF2 (10k iterations)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
63.1ms
19MB
Deno
72ms
33MB
esrun
101.7ms
26MB
LLRT
99.2ms
11MB
Node.js
73.2ms
46MB
fetch (local server)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.9ms
21MB
Deno
36ms
42MB
esrun
43.3ms
39MB
LLRT
20.4ms
11MB
Node.js
100ms
89MB
File read (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
36.9ms
32MB
Deno
39.6ms
41MB
esrun
45.8ms
37MB
LLRT
27.9ms
11MB
Node.js
114.5ms
54MB
File read (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.3ms
38MB
Deno
68.7ms
166MB
esrun
28.3ms
92MB
LLRT
14.1ms
15MB
Node.js
60.1ms
112MB
File write (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.6ms
22MB
Deno
93.6ms
40MB
esrun
90.1ms
32MB
LLRT
97.7ms
11MB
Node.js
155.1ms
55MB
File write (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
21.5ms
24MB
Deno
46.1ms
80MB
esrun
39.6ms
33MB
LLRT
53.1ms
14MB
Node.js
59.2ms
91MB
File append (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.4ms
24MB
Deno
41.9ms
41MB
esrun
38.7ms
32MB
LLRT
—
—
Node.js
93.6ms
55MB
File append (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7ms
20MB
Deno
15.8ms
50MB
esrun
11.1ms
28MB
LLRT
—
—
Node.js
23.7ms
60MB
File stat (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
49.5ms
24MB
Deno
94.9ms
39MB
esrun
56.3ms
33MB
LLRT
41.3ms
11MB
Node.js
66.8ms
49MB
File stat (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
200ms
32MB
Deno
365.9ms
44MB
esrun
251.1ms
39MB
LLRT
161.7ms
11MB
Node.js
269.3ms
59MB
File exists (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.7ms
25MB
Deno
94.3ms
40MB
esrun
42.6ms
32MB
LLRT
46.9ms
11MB
Node.js
61.8ms
48MB
File exists (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
32.5ms
36MB
Deno
385.5ms
44MB
esrun
198.9ms
34MB
LLRT
194.8ms
11MB
Node.js
242.5ms
60MB
Glob scan
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
27ms
30MB
Deno
—
—
esrun
47.8ms
35MB
LLRT
—
—
Node.js
194.7ms
63MB
Spawn a child process
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
98.3ms
24MB
Deno
108.5ms
50MB
esrun
84.2ms
34MB
LLRT
94ms
11MB
Node.js
208.1ms
53MB
See full benchmarks →

Why ES-Runtime?

Connect

HTTP and WebSocket servers in the binary.

Store

Embedded SQLite plus first-party Postgres, MySQL/MariaDB, Redis, and Valkey.

Speak

Parsers for every format your API touches.

Ships with

Native image processing and durable state.

Secure

Deny by default on a capability-based runtime.

Develop

Tests with DOM parity, real-browser runs, watch, HMR, plugins.

Run on

A Rust core around V8, scripted in JavaScript and TypeScript.

Built-in Utils

SMTP mail, archive handling, password-grade hashing, two-factor codes, cookies, CSRF tokens, and webhooks.

Message

AMQP 0-9-1, MQTT and NATS messaging.

Observe

OTLP spans to any collector, no SDK.

Choosing a runtime? How esrun compares to Node.js, Bun and Deno →

State without infrastructure

Durable workers: addressable state that outlives the process.

CALLERSONE DURABLE WORKERITS FILE · SQLITEAnaBenChloétake(1)take(1)take(1)Stock("hoodie")MAILBOXRUNNINGSTATEAnaBenChloéidleleft: 2on diskgone with the processanswer only after commit⚡ SIGKILLhoodie.dbleft: 2untouchedThree customers want the last two hoodies. Their calls queue in one worker's mailbox.

One call at a time, per worker

Calls to one worker queue and run in order, so two customers cannot both take the last item. No lock to take, no row to contend for.

Answered once it is on disk

A result is released only after the writes behind it have committed. A crash is a call that never returned, never one that returned a lie.

Timers that outlive the process

An alarm is stored beside the state and runs after a restart: retries, abandoned carts, a nightly roll-up, without a scheduler service.

Nothing else to run

State lives in embedded SQLite, one file per worker, under the same read and write grants as everything else. No Redis, no queue, no database server.

Killed with SIGKILL under load, the example shop loses no order it acknowledged.

Benchmarks

Hono hello-world · Speed & Memory
Runtime
Throughput (higher ↑)
Memory (lower ↓)
Deno
69.4k
50 MB
Bun
67.2k
45 MB
esrun
47.8k
52 MB
Node.js
28.6k
111 MB
Elysia hello-world · Speed & Memory
Runtime
Throughput (higher ↑)
Memory (lower ↓)
Bun
81.2k
43 MB
Deno
74.2k
56 MB
esrun
49.3k
58 MB
Node.js
30.1k
97 MB

Plaintext hello-world over loopback, driven by oha at 100 connections — best of three runs. Elysia is measured on the esdev bundle every runtime serves (esrun is ESM-only), and Node serves both frameworks through the same @hono/node-server glue, so the delta is route handling, not the adapter. How it is measured →

Deny by default

A run reaches what the command line named, and nothing else.

RV8 ISOLATEOP BOUNDARY · RUSTTHE HOSTfetch(url)file(path).text()serve(opts, fn)write(path, bytes)env.DATABASE_URLnew Command("sh")netreadlistenwriteenvrunapi.example.com./config.json:8080./out/report.csvDATABASE_URL/bin/shesrun --allow-net --allow-read --allow-listen app.js

Eleven capabilities: read, write, imports, net, listen, env, run, signals, workers, diagnostics, diagnostics-detail. The gate is the op, not the import.

Two binaries.

esrun runs your service and nothing else — no watcher, no debugger port, no transform. That narrowness is what a capability flag is worth.

esdev pays for it on your machine: same runtime, same flags, plus the toolchain to get there.

In esdev, not in production

  • TypeScript & JSX
  • CommonJS (converted by esdev)
  • Bundler
  • Test runner
  • Watch mode
  • Debugger
  • Permission discovery

Not goals at all

  • Node.js compatibility
  • CommonJS at runtime
  • Package installer
  • Linter / formatter
  • FFI
  • Native addons
  • Remote module imports
  • SharedWorker / data: worker URLs

Ship a runtime you can trust.

Start building

Install both binaries with one command, and run your first module. Standards-based, fast, ESM-only.

Ship it locked down

Nothing is granted unless the command line says so. Build the deploy line that states exactly what your service may touch.