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.2ms
29MB
esrun
8.6ms
25MB
LLRT
3.6ms
11MB
Node.js
21.6ms
41MB
Parse + run ~100 KB script
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
14.5ms
16MB
Deno
21.8ms
30MB
esrun
20.7ms
26MB
LLRT
11.9ms
11MB
Node.js
33.9ms
44MB
Load a 300-module graph
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.3ms
24MB
Deno
32.7ms
33MB
esrun
33.6ms
27MB
LLRT
15.4ms
11MB
Node.js
99.2ms
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
140MB
Deno
123MB
esrun
107MB
LLRT
157MB
Node.js
134MB
Tight compute loop
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
113.7ms
22MB
Deno
238.3ms
37MB
esrun
194ms
34MB
LLRT
1754.2ms
11MB
Node.js
202ms
52MB
JSON parse/stringify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
165.8ms
38MB
Deno
208.2ms
53MB
esrun
196.7ms
52MB
LLRT
653.3ms
11MB
Node.js
272.3ms
78MB
JSON (large documents)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
364.4ms
130MB
Deno
512.3ms
136MB
esrun
477ms
92MB
LLRT
1613.7ms
125MB
Node.js
685.9ms
157MB
Regex (match, validate, replace)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.5ms
32MB
Deno
63.4ms
38MB
esrun
58.8ms
37MB
LLRT
984.4ms
11MB
Node.js
66ms
54MB
String building and slicing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
65.6ms
41MB
Deno
62.1ms
46MB
esrun
59.6ms
44MB
LLRT
163.1ms
11MB
Node.js
60.4ms
63MB
structuredClone
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
236.2ms
29MB
Deno
272.5ms
42MB
esrun
280.8ms
50MB
LLRT
410.6ms
11MB
Node.js
226.1ms
53MB
throw/catch + stack capture
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
344.9ms
41MB
Deno
4320ms
62MB
esrun
379.1ms
45MB
LLRT
380.6ms
11MB
Node.js
1440.6ms
62MB
async/await throughput
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
39.2ms
31MB
Deno
30.9ms
38MB
esrun
31.1ms
36MB
LLRT
573.9ms
11MB
Node.js
55.9ms
53MB
setTimeout churn
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.4ms
45MB
Deno
251.3ms
166MB
esrun
115.3ms
86MB
LLRT
46.9ms
48MB
Node.js
49.5ms
84MB
URL parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.3ms
46MB
Deno
102.5ms
45MB
esrun
89.1ms
44MB
LLRT
125.6ms
11MB
Node.js
34.6ms
63MB
TextEncoder/TextDecoder (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.2ms
35MB
Deno
73.2ms
48MB
esrun
73.2ms
46MB
LLRT
67.8ms
11MB
Node.js
68.7ms
64MB
TextEncoder/TextDecoder (64 KiB)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
69.6ms
37MB
Deno
149.3ms
39MB
esrun
116.3ms
38MB
LLRT
253.9ms
11MB
Node.js
315.5ms
53MB
base64 (atob/btoa)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
10.9ms
32MB
Deno
7.5ms
40MB
esrun
22.6ms
36MB
LLRT
33.8ms
11MB
Node.js
6.8ms
53MB
TypedArray / DataView
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
16.3ms
33MB
Deno
13.4ms
39MB
esrun
13.2ms
37MB
LLRT
71.3ms
11MB
Node.js
14.1ms
54MB
Headers / Request parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
234.8ms
35MB
Deno
1590.8ms
125MB
esrun
443.1ms
47MB
LLRT
—
—
Node.js
449.2ms
74MB
Multipart FormData round trip
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
19.7ms
44MB
Deno
384.2ms
110MB
esrun
95.1ms
42MB
LLRT
4596.7ms
11MB
Node.js
274.3ms
120MB
Date + Intl formatting
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
81.8ms
38MB
Deno
138.9ms
45MB
esrun
136.9ms
41MB
LLRT
—
—
Node.js
124.3ms
61MB
ReadableStream piping
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
5.7ms
24MB
Deno
10.5ms
42MB
esrun
10.3ms
42MB
LLRT
95.7ms
11MB
Node.js
11.6ms
62MB
SubtleCrypto SHA-256
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
429.1ms
35MB
Deno
424.1ms
40MB
esrun
346.8ms
33MB
LLRT
337.6ms
11MB
Node.js
519.8ms
56MB
HMAC + AES-GCM
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
86.3ms
31MB
Deno
98.5ms
41MB
esrun
34.9ms
35MB
LLRT
35.9ms
11MB
Node.js
188.3ms
61MB
ECDSA P-256 sign + verify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
209.1ms
24MB
Deno
2185.7ms
40MB
esrun
1162.6ms
34MB
LLRT
1048.8ms
11MB
Node.js
370.6ms
57MB
PBKDF2 (10k iterations)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
65ms
19MB
Deno
74.8ms
32MB
esrun
103.2ms
26MB
LLRT
99.9ms
11MB
Node.js
74ms
46MB
fetch (local server)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.8ms
21MB
Deno
36.6ms
41MB
esrun
43ms
38MB
LLRT
18.3ms
11MB
Node.js
100.8ms
88MB
File read (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
37.3ms
33MB
Deno
39.5ms
40MB
esrun
42ms
37MB
LLRT
27.2ms
11MB
Node.js
109.7ms
55MB
File read (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.8ms
36MB
Deno
68.3ms
165MB
esrun
28.4ms
91MB
LLRT
12.7ms
14MB
Node.js
60ms
112MB
File write (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.5ms
22MB
Deno
85.1ms
40MB
esrun
77.5ms
32MB
LLRT
89.8ms
11MB
Node.js
143.1ms
55MB
File write (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
21.2ms
25MB
Deno
43.8ms
80MB
esrun
35.3ms
33MB
LLRT
52.9ms
15MB
Node.js
56.7ms
91MB
File append (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.5ms
24MB
Deno
41.7ms
40MB
esrun
36.1ms
32MB
LLRT
—
—
Node.js
99.2ms
55MB
File append (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.5ms
20MB
Deno
15.7ms
50MB
esrun
8.9ms
28MB
LLRT
—
—
Node.js
25.5ms
60MB
File stat (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
47.9ms
24MB
Deno
95.3ms
39MB
esrun
56.4ms
33MB
LLRT
42.2ms
11MB
Node.js
72.9ms
49MB
File stat (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
210.4ms
32MB
Deno
382.6ms
44MB
esrun
240.9ms
39MB
LLRT
168.1ms
11MB
Node.js
277.2ms
59MB
File exists (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.7ms
25MB
Deno
97.6ms
39MB
esrun
40.8ms
32MB
LLRT
47.4ms
11MB
Node.js
59.8ms
49MB
File exists (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
33.1ms
36MB
Deno
378.2ms
44MB
esrun
187.7ms
33MB
LLRT
206.4ms
11MB
Node.js
236.1ms
59MB
Glob scan
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
28.5ms
30MB
Deno
—
—
esrun
48.9ms
34MB
LLRT
—
—
Node.js
199.4ms
65MB
Spawn a child process
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
99.9ms
24MB
Deno
116.8ms
51MB
esrun
87.6ms
33MB
LLRT
94.6ms
11MB
Node.js
221.4ms
53MB
Cold start (near-empty script)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
4.2ms
12MB
Deno
13.2ms
29MB
esrun
8.6ms
25MB
LLRT
3.6ms
11MB
Node.js
21.6ms
41MB
Parse + run ~100 KB script
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
14.5ms
16MB
Deno
21.8ms
30MB
esrun
20.7ms
26MB
LLRT
11.9ms
11MB
Node.js
33.9ms
44MB
Load a 300-module graph
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.3ms
24MB
Deno
32.7ms
33MB
esrun
33.6ms
27MB
LLRT
15.4ms
11MB
Node.js
99.2ms
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
140MB
Deno
123MB
esrun
107MB
LLRT
157MB
Node.js
134MB
Tight compute loop
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
113.7ms
22MB
Deno
238.3ms
37MB
esrun
194ms
34MB
LLRT
1754.2ms
11MB
Node.js
202ms
52MB
JSON parse/stringify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
165.8ms
38MB
Deno
208.2ms
53MB
esrun
196.7ms
52MB
LLRT
653.3ms
11MB
Node.js
272.3ms
78MB
JSON (large documents)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
364.4ms
130MB
Deno
512.3ms
136MB
esrun
477ms
92MB
LLRT
1613.7ms
125MB
Node.js
685.9ms
157MB
Regex (match, validate, replace)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.5ms
32MB
Deno
63.4ms
38MB
esrun
58.8ms
37MB
LLRT
984.4ms
11MB
Node.js
66ms
54MB
String building and slicing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
65.6ms
41MB
Deno
62.1ms
46MB
esrun
59.6ms
44MB
LLRT
163.1ms
11MB
Node.js
60.4ms
63MB
structuredClone
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
236.2ms
29MB
Deno
272.5ms
42MB
esrun
280.8ms
50MB
LLRT
410.6ms
11MB
Node.js
226.1ms
53MB
throw/catch + stack capture
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
344.9ms
41MB
Deno
4320ms
62MB
esrun
379.1ms
45MB
LLRT
380.6ms
11MB
Node.js
1440.6ms
62MB
async/await throughput
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
39.2ms
31MB
Deno
30.9ms
38MB
esrun
31.1ms
36MB
LLRT
573.9ms
11MB
Node.js
55.9ms
53MB
setTimeout churn
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.4ms
45MB
Deno
251.3ms
166MB
esrun
115.3ms
86MB
LLRT
46.9ms
48MB
Node.js
49.5ms
84MB
URL parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.3ms
46MB
Deno
102.5ms
45MB
esrun
89.1ms
44MB
LLRT
125.6ms
11MB
Node.js
34.6ms
63MB
TextEncoder/TextDecoder (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.2ms
35MB
Deno
73.2ms
48MB
esrun
73.2ms
46MB
LLRT
67.8ms
11MB
Node.js
68.7ms
64MB
TextEncoder/TextDecoder (64 KiB)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
69.6ms
37MB
Deno
149.3ms
39MB
esrun
116.3ms
38MB
LLRT
253.9ms
11MB
Node.js
315.5ms
53MB
base64 (atob/btoa)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
10.9ms
32MB
Deno
7.5ms
40MB
esrun
22.6ms
36MB
LLRT
33.8ms
11MB
Node.js
6.8ms
53MB
TypedArray / DataView
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
16.3ms
33MB
Deno
13.4ms
39MB
esrun
13.2ms
37MB
LLRT
71.3ms
11MB
Node.js
14.1ms
54MB
Headers / Request parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
234.8ms
35MB
Deno
1590.8ms
125MB
esrun
443.1ms
47MB
LLRT
—
—
Node.js
449.2ms
74MB
Multipart FormData round trip
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
19.7ms
44MB
Deno
384.2ms
110MB
esrun
95.1ms
42MB
LLRT
4596.7ms
11MB
Node.js
274.3ms
120MB
Date + Intl formatting
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
81.8ms
38MB
Deno
138.9ms
45MB
esrun
136.9ms
41MB
LLRT
—
—
Node.js
124.3ms
61MB
ReadableStream piping
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
5.7ms
24MB
Deno
10.5ms
42MB
esrun
10.3ms
42MB
LLRT
95.7ms
11MB
Node.js
11.6ms
62MB
SubtleCrypto SHA-256
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
429.1ms
35MB
Deno
424.1ms
40MB
esrun
346.8ms
33MB
LLRT
337.6ms
11MB
Node.js
519.8ms
56MB
HMAC + AES-GCM
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
86.3ms
31MB
Deno
98.5ms
41MB
esrun
34.9ms
35MB
LLRT
35.9ms
11MB
Node.js
188.3ms
61MB
ECDSA P-256 sign + verify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
209.1ms
24MB
Deno
2185.7ms
40MB
esrun
1162.6ms
34MB
LLRT
1048.8ms
11MB
Node.js
370.6ms
57MB
PBKDF2 (10k iterations)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
65ms
19MB
Deno
74.8ms
32MB
esrun
103.2ms
26MB
LLRT
99.9ms
11MB
Node.js
74ms
46MB
fetch (local server)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.8ms
21MB
Deno
36.6ms
41MB
esrun
43ms
38MB
LLRT
18.3ms
11MB
Node.js
100.8ms
88MB
File read (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
37.3ms
33MB
Deno
39.5ms
40MB
esrun
42ms
37MB
LLRT
27.2ms
11MB
Node.js
109.7ms
55MB
File read (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.8ms
36MB
Deno
68.3ms
165MB
esrun
28.4ms
91MB
LLRT
12.7ms
14MB
Node.js
60ms
112MB
File write (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.5ms
22MB
Deno
85.1ms
40MB
esrun
77.5ms
32MB
LLRT
89.8ms
11MB
Node.js
143.1ms
55MB
File write (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
21.2ms
25MB
Deno
43.8ms
80MB
esrun
35.3ms
33MB
LLRT
52.9ms
15MB
Node.js
56.7ms
91MB
File append (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.5ms
24MB
Deno
41.7ms
40MB
esrun
36.1ms
32MB
LLRT
—
—
Node.js
99.2ms
55MB
File append (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.5ms
20MB
Deno
15.7ms
50MB
esrun
8.9ms
28MB
LLRT
—
—
Node.js
25.5ms
60MB
File stat (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
47.9ms
24MB
Deno
95.3ms
39MB
esrun
56.4ms
33MB
LLRT
42.2ms
11MB
Node.js
72.9ms
49MB
File stat (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
210.4ms
32MB
Deno
382.6ms
44MB
esrun
240.9ms
39MB
LLRT
168.1ms
11MB
Node.js
277.2ms
59MB
File exists (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.7ms
25MB
Deno
97.6ms
39MB
esrun
40.8ms
32MB
LLRT
47.4ms
11MB
Node.js
59.8ms
49MB
File exists (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
33.1ms
36MB
Deno
378.2ms
44MB
esrun
187.7ms
33MB
LLRT
206.4ms
11MB
Node.js
236.1ms
59MB
Glob scan
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
28.5ms
30MB
Deno
—
—
esrun
48.9ms
34MB
LLRT
—
—
Node.js
199.4ms
65MB
Spawn a child process
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
99.9ms
24MB
Deno
116.8ms
51MB
esrun
87.6ms
33MB
LLRT
94.6ms
11MB
Node.js
221.4ms
53MB
See full benchmarks →

Why ES-Runtime?

Connect

HTTP and WebSocket servers in the binary.

Store

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

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, password-grade hashing, two-factor codes, cookies, CSRF tokens, and webhooks.

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
73.3k
51 MB
Bun
70.2k
45 MB
esrun
48.9k
53 MB
Node.js
30.1k
111 MB
Elysia hello-world · Speed & Memory
Runtime
Throughput (higher ↑)
Memory (lower ↓)
Bun
84.9k
46 MB
Deno
75.7k
60 MB
esrun
50.0k
56 MB
Node.js
30.3k
96 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.