Introduction
Fairyfly is a lightweight backend JavaScript runtime built with Zig and QuickJS. It runs on a single OS thread with an event loop powered by libxev (io_uring on Linux by default, epoll fallback, kqueue on macOS).
Fairyfly is designed first for serverless and edge-style workloads: short-lived processes, fast cold starts, tight memory budgets, and one binary that exits when the event loop drains. It is a native POSIX binary, so it fits container-based serverless platforms and edge VMs rather than V8-isolate runtimes.
165k req/sec · ~10ms guest cold start · 5M reqs at 500 concurrency on 2 vCPU / 1GB microVM
When to use Fairyfly
- Cold start matters (CLI tools, edge functions, short-lived jobs)
- Memory is capped (strict container limits)
- You want the whole runtime to fit in your head (~24k lines of Zig across 54 files)
When NOT to use Fairyfly
- Browser parity — no DOM, no
windowobject - npm ecosystem — no automatic
node_modulesresolution
If you need those, use Node.js, Bun, or Deno instead.
What's inside
Everything ships in a single binary. No npm install needed for core features.
Three rules
Rule 1: ESM only, extensions optional
Imports are ESM (there is no require). A relative specifier may omit
the .js extension — the loader then probes X.js, then X/index.js:
Non-.js extensions (.mjs, …) are not probed — write those out.
Bare specifiers like import "lodash" resolve by walking up node_modules/ directories, but only pure-JS ESM packages work.
Rule 2: No CommonJS, no require()
There is no require() function. No module.exports. No __dirname or __filename globals.
Use import/export and import.meta.url instead.
Rule 3: No process.nextTick
Use queueMicrotask() instead. It runs before the next timer or I/O event, just like process.nextTick in Node.js.
Output:
