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.3ms
28MB
esrun
8.6ms
25MB
LLRT
3.6ms
11MB
Node.js
22.3ms
41MB
Parse + run ~100 KB script
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
14.7ms
16MB
Deno
22ms
29MB
esrun
20.6ms
26MB
LLRT
11.9ms
11MB
Node.js
34.1ms
44MB
Load a 300-module graph
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.4ms
24MB
Deno
32.1ms
33MB
esrun
34ms
27MB
LLRT
15.7ms
11MB
Node.js
99.3ms
61MB
Peak resident memory (idle)
Runtime
Perf (lower is better)
Bun
12MB
Deno
28MB
esrun
25MB
LLRT
11MB
Node.js
41MB
Peak resident memory (under load)
Runtime
Perf (lower is better)
Bun
138MB
Deno
122MB
esrun
106MB
LLRT
157MB
Node.js
134MB
Tight compute loop
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
110.4ms
22MB
Deno
241.2ms
37MB
esrun
194.7ms
34MB
LLRT
1782.2ms
11MB
Node.js
197.8ms
53MB
JSON parse/stringify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
152ms
37MB
Deno
201.2ms
54MB
esrun
196ms
52MB
LLRT
662.3ms
11MB
Node.js
265.6ms
78MB
JSON (large documents)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
369.6ms
116MB
Deno
520.2ms
136MB
esrun
491.4ms
91MB
LLRT
1648.5ms
122MB
Node.js
699.7ms
156MB
Regex (match, validate, replace)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.8ms
32MB
Deno
64.3ms
39MB
esrun
61.2ms
37MB
LLRT
977.5ms
11MB
Node.js
66.3ms
54MB
String building and slicing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
60.9ms
42MB
Deno
61.4ms
46MB
esrun
58.9ms
44MB
LLRT
164.7ms
11MB
Node.js
63.4ms
63MB
structuredClone
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
240.9ms
29MB
Deno
266.8ms
42MB
esrun
288.9ms
50MB
LLRT
414.1ms
11MB
Node.js
218.1ms
54MB
throw/catch + stack capture
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
347.9ms
41MB
Deno
4290.1ms
62MB
esrun
386.4ms
45MB
LLRT
380.9ms
11MB
Node.js
1494.7ms
62MB
async/await throughput
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
39.8ms
31MB
Deno
30.8ms
37MB
esrun
31.6ms
37MB
LLRT
581.8ms
11MB
Node.js
56.6ms
53MB
setTimeout churn
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.9ms
45MB
Deno
245.4ms
166MB
esrun
118.7ms
86MB
LLRT
47.1ms
44MB
Node.js
51.2ms
84MB
URL parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.8ms
46MB
Deno
103ms
46MB
esrun
88.8ms
44MB
LLRT
124.5ms
11MB
Node.js
35.6ms
63MB
TextEncoder/TextDecoder (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.4ms
36MB
Deno
71.8ms
49MB
esrun
66.1ms
46MB
LLRT
70.5ms
11MB
Node.js
69.5ms
64MB
TextEncoder/TextDecoder (64 KiB)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
69.2ms
36MB
Deno
148.6ms
38MB
esrun
110.6ms
38MB
LLRT
259.1ms
11MB
Node.js
320.1ms
53MB
base64 (atob/btoa)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
11.1ms
32MB
Deno
7.9ms
40MB
esrun
23.2ms
36MB
LLRT
34.1ms
11MB
Node.js
7.1ms
53MB
TypedArray / DataView
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
16.1ms
33MB
Deno
13.4ms
40MB
esrun
13.4ms
37MB
LLRT
73.5ms
11MB
Node.js
14.3ms
54MB
Headers / Request parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
237.4ms
36MB
Deno
1597.8ms
124MB
esrun
454.3ms
47MB
LLRT
—
—
Node.js
452.6ms
74MB
Multipart FormData round trip
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
19ms
44MB
Deno
376.6ms
111MB
esrun
87.6ms
42MB
LLRT
4395.5ms
11MB
Node.js
272.2ms
121MB
Date + Intl formatting
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
79ms
38MB
Deno
139.9ms
46MB
esrun
138.1ms
42MB
LLRT
—
—
Node.js
124.2ms
61MB
ReadableStream piping
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
5.7ms
25MB
Deno
10.7ms
43MB
esrun
10.3ms
42MB
LLRT
94.2ms
11MB
Node.js
11.1ms
62MB
SubtleCrypto SHA-256
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
409.6ms
35MB
Deno
399.3ms
40MB
esrun
332.2ms
33MB
LLRT
328.1ms
11MB
Node.js
501.1ms
57MB
HMAC + AES-GCM
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
79.2ms
31MB
Deno
91.8ms
40MB
esrun
34.1ms
35MB
LLRT
35.4ms
11MB
Node.js
177.9ms
61MB
ECDSA P-256 sign + verify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
189.6ms
24MB
Deno
2192.2ms
41MB
esrun
1117.4ms
34MB
LLRT
1014.5ms
11MB
Node.js
408.6ms
57MB
PBKDF2 (10k iterations)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
62.4ms
19MB
Deno
70.3ms
32MB
esrun
98.4ms
27MB
LLRT
96.3ms
11MB
Node.js
70ms
46MB
fetch (local server)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.2ms
20MB
Deno
34.8ms
41MB
esrun
41.6ms
38MB
LLRT
17.9ms
11MB
Node.js
97.2ms
90MB
File read (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
36.1ms
33MB
Deno
38.9ms
40MB
esrun
41.2ms
36MB
LLRT
26.2ms
11MB
Node.js
109.2ms
55MB
File read (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.6ms
36MB
Deno
64.4ms
165MB
esrun
27.4ms
91MB
LLRT
13.3ms
15MB
Node.js
59.9ms
112MB
File write (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.5ms
22MB
Deno
82.4ms
40MB
esrun
75.8ms
32MB
LLRT
88.8ms
11MB
Node.js
142.3ms
55MB
File write (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
20.2ms
25MB
Deno
40.1ms
79MB
esrun
38.6ms
32MB
LLRT
51.8ms
14MB
Node.js
56.9ms
91MB
File append (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.6ms
24MB
Deno
39.7ms
40MB
esrun
35.5ms
32MB
LLRT
—
—
Node.js
93.5ms
55MB
File append (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.5ms
20MB
Deno
15.3ms
49MB
esrun
8.4ms
28MB
LLRT
—
—
Node.js
23ms
60MB
File stat (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
48.8ms
25MB
Deno
89.2ms
39MB
esrun
54ms
32MB
LLRT
40ms
11MB
Node.js
64.2ms
49MB
File stat (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
199.2ms
32MB
Deno
357.3ms
44MB
esrun
233ms
39MB
LLRT
157.5ms
11MB
Node.js
261.6ms
59MB
File exists (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.6ms
25MB
Deno
88.8ms
39MB
esrun
39.3ms
32MB
LLRT
46.6ms
11MB
Node.js
58.8ms
49MB
File exists (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.9ms
36MB
Deno
362.2ms
44MB
esrun
176.9ms
33MB
LLRT
184.5ms
11MB
Node.js
249.5ms
59MB
Glob scan
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
24.7ms
30MB
Deno
—
—
esrun
47.3ms
34MB
LLRT
—
—
Node.js
181.7ms
65MB
Spawn a child process
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
96ms
24MB
Deno
105.2ms
50MB
esrun
79.3ms
34MB
LLRT
93.3ms
11MB
Node.js
200.9ms
53MB
Cold start (near-empty script)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
4.2ms
12MB
Deno
13.3ms
28MB
esrun
8.6ms
25MB
LLRT
3.6ms
11MB
Node.js
22.3ms
41MB
Parse + run ~100 KB script
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
14.7ms
16MB
Deno
22ms
29MB
esrun
20.6ms
26MB
LLRT
11.9ms
11MB
Node.js
34.1ms
44MB
Load a 300-module graph
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.4ms
24MB
Deno
32.1ms
33MB
esrun
34ms
27MB
LLRT
15.7ms
11MB
Node.js
99.3ms
61MB
Peak resident memory (idle)
Runtime
Perf (lower is better)
Bun
12MB
Deno
28MB
esrun
25MB
LLRT
11MB
Node.js
41MB
Peak resident memory (under load)
Runtime
Perf (lower is better)
Bun
138MB
Deno
122MB
esrun
106MB
LLRT
157MB
Node.js
134MB
Tight compute loop
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
110.4ms
22MB
Deno
241.2ms
37MB
esrun
194.7ms
34MB
LLRT
1782.2ms
11MB
Node.js
197.8ms
53MB
JSON parse/stringify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
152ms
37MB
Deno
201.2ms
54MB
esrun
196ms
52MB
LLRT
662.3ms
11MB
Node.js
265.6ms
78MB
JSON (large documents)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
369.6ms
116MB
Deno
520.2ms
136MB
esrun
491.4ms
91MB
LLRT
1648.5ms
122MB
Node.js
699.7ms
156MB
Regex (match, validate, replace)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.8ms
32MB
Deno
64.3ms
39MB
esrun
61.2ms
37MB
LLRT
977.5ms
11MB
Node.js
66.3ms
54MB
String building and slicing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
60.9ms
42MB
Deno
61.4ms
46MB
esrun
58.9ms
44MB
LLRT
164.7ms
11MB
Node.js
63.4ms
63MB
structuredClone
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
240.9ms
29MB
Deno
266.8ms
42MB
esrun
288.9ms
50MB
LLRT
414.1ms
11MB
Node.js
218.1ms
54MB
throw/catch + stack capture
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
347.9ms
41MB
Deno
4290.1ms
62MB
esrun
386.4ms
45MB
LLRT
380.9ms
11MB
Node.js
1494.7ms
62MB
async/await throughput
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
39.8ms
31MB
Deno
30.8ms
37MB
esrun
31.6ms
37MB
LLRT
581.8ms
11MB
Node.js
56.6ms
53MB
setTimeout churn
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.9ms
45MB
Deno
245.4ms
166MB
esrun
118.7ms
86MB
LLRT
47.1ms
44MB
Node.js
51.2ms
84MB
URL parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.8ms
46MB
Deno
103ms
46MB
esrun
88.8ms
44MB
LLRT
124.5ms
11MB
Node.js
35.6ms
63MB
TextEncoder/TextDecoder (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.4ms
36MB
Deno
71.8ms
49MB
esrun
66.1ms
46MB
LLRT
70.5ms
11MB
Node.js
69.5ms
64MB
TextEncoder/TextDecoder (64 KiB)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
69.2ms
36MB
Deno
148.6ms
38MB
esrun
110.6ms
38MB
LLRT
259.1ms
11MB
Node.js
320.1ms
53MB
base64 (atob/btoa)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
11.1ms
32MB
Deno
7.9ms
40MB
esrun
23.2ms
36MB
LLRT
34.1ms
11MB
Node.js
7.1ms
53MB
TypedArray / DataView
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
16.1ms
33MB
Deno
13.4ms
40MB
esrun
13.4ms
37MB
LLRT
73.5ms
11MB
Node.js
14.3ms
54MB
Headers / Request parsing
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
237.4ms
36MB
Deno
1597.8ms
124MB
esrun
454.3ms
47MB
LLRT
—
—
Node.js
452.6ms
74MB
Multipart FormData round trip
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
19ms
44MB
Deno
376.6ms
111MB
esrun
87.6ms
42MB
LLRT
4395.5ms
11MB
Node.js
272.2ms
121MB
Date + Intl formatting
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
79ms
38MB
Deno
139.9ms
46MB
esrun
138.1ms
42MB
LLRT
—
—
Node.js
124.2ms
61MB
ReadableStream piping
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
5.7ms
25MB
Deno
10.7ms
43MB
esrun
10.3ms
42MB
LLRT
94.2ms
11MB
Node.js
11.1ms
62MB
SubtleCrypto SHA-256
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
409.6ms
35MB
Deno
399.3ms
40MB
esrun
332.2ms
33MB
LLRT
328.1ms
11MB
Node.js
501.1ms
57MB
HMAC + AES-GCM
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
79.2ms
31MB
Deno
91.8ms
40MB
esrun
34.1ms
35MB
LLRT
35.4ms
11MB
Node.js
177.9ms
61MB
ECDSA P-256 sign + verify
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
189.6ms
24MB
Deno
2192.2ms
41MB
esrun
1117.4ms
34MB
LLRT
1014.5ms
11MB
Node.js
408.6ms
57MB
PBKDF2 (10k iterations)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
62.4ms
19MB
Deno
70.3ms
32MB
esrun
98.4ms
27MB
LLRT
96.3ms
11MB
Node.js
70ms
46MB
fetch (local server)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
18.2ms
20MB
Deno
34.8ms
41MB
esrun
41.6ms
38MB
LLRT
17.9ms
11MB
Node.js
97.2ms
90MB
File read (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
36.1ms
33MB
Deno
38.9ms
40MB
esrun
41.2ms
36MB
LLRT
26.2ms
11MB
Node.js
109.2ms
55MB
File read (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
17.6ms
36MB
Deno
64.4ms
165MB
esrun
27.4ms
91MB
LLRT
13.3ms
15MB
Node.js
59.9ms
112MB
File write (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
12.5ms
22MB
Deno
82.4ms
40MB
esrun
75.8ms
32MB
LLRT
88.8ms
11MB
Node.js
142.3ms
55MB
File write (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
20.2ms
25MB
Deno
40.1ms
79MB
esrun
38.6ms
32MB
LLRT
51.8ms
14MB
Node.js
56.9ms
91MB
File append (small)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
30.6ms
24MB
Deno
39.7ms
40MB
esrun
35.5ms
32MB
LLRT
—
—
Node.js
93.5ms
55MB
File append (large)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.5ms
20MB
Deno
15.3ms
49MB
esrun
8.4ms
28MB
LLRT
—
—
Node.js
23ms
60MB
File stat (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
48.8ms
25MB
Deno
89.2ms
39MB
esrun
54ms
32MB
LLRT
40ms
11MB
Node.js
64.2ms
49MB
File stat (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
199.2ms
32MB
Deno
357.3ms
44MB
esrun
233ms
39MB
LLRT
157.5ms
11MB
Node.js
261.6ms
59MB
File exists (one path)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
7.6ms
25MB
Deno
88.8ms
39MB
esrun
39.3ms
32MB
LLRT
46.6ms
11MB
Node.js
58.8ms
49MB
File exists (1000 paths)
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
31.9ms
36MB
Deno
362.2ms
44MB
esrun
176.9ms
33MB
LLRT
184.5ms
11MB
Node.js
249.5ms
59MB
Glob scan
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
24.7ms
30MB
Deno
—
—
esrun
47.3ms
34MB
LLRT
—
—
Node.js
181.7ms
65MB
Spawn a child process
Runtime
Perf (lower is better)
Memory (lower ↓)
Bun
96ms
24MB
Deno
105.2ms
50MB
esrun
79.3ms
34MB
LLRT
93.3ms
11MB
Node.js
200.9ms
53MB
See full benchmarks →

Why ES-Runtime?

Connect

HTTP and WebSocket servers in the binary.

Store

Embedded SQLite plus first-party Postgres, MySQL, 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, and cookies.

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.