// HACKER NEWS — CYBERSECURITY
Kino: A high-performance Ractor web server for Ruby 4.0
Kino is a high-performance Ractor web server for Ruby 4.0+.
Ruby threads cannot run Ruby code in parallel, so production setups fork
a process per core and pay for each copy in memory. Kino runs your code
on every core in one small process. A Rust (tokio + hyper)
front-end owns the network, parallel Ractors run your Rack 3 app,
and a threaded fallback mode runs everything else, Rails included.
N.B.: Ractors are officially experimental in Ruby 4.0, and so is this server. The threaded mode is solid. Still, Kino aims to be the best way to experiment with Ractors today—and the best Ractor server when they become stable.
The GVL allows only one Ruby thread to run at a time. To use all cores,
Ruby servers fork processes, and every fork costs a full copy of the
app. Ractors do not have this limit: each one has its own lock, so one
process can run Ruby in parallel. What was missing is a server that
dispatches requests to them. Ruby 4.0 reworked Ractors (Ractor::Port,
shareable_proc, less lock contention) and made this worth building.
Why a Ractor server has to be built this way, and which Rust parts make
Ractors fast here: doc/why-kino.md. The full design
notes live in doc/architecture.md.
Measured on a real server: AWS c7a.2xlarge (8-core AMD EPYC 9R14,
16 GB, Amazon Linux 2023). This is a realistic app-server size.
These tables run a tiny synthetic Rack app—plaintext, a 10 KB body,
a CPU-bound fib, a 5 ms wait—deliberately small, to measure the server
rather than an app. It is Ractor-shareable, so Kino runs it in :ractor
mode (and :threaded for comparison). A real Rails app is a different
story: it is not Ractor-shareable, so it runs only in Kino's
:threaded fallback, with its own numbers—see Rails below.
Ruby 4.0.5 with YJIT, every server at its defaults: Puma forks 8 workers ×
3 threads, Kino stays in one process (8 workers; 1 thread each in ractor
modes, 3 in threaded). Numbers are req/s by wrk (8-second windows, 64
connections, same host). Methodology:
doc/benchmarks.md.
Memory tells two different stories depending on the app, both by PSS
(proportional set size; see note) after sustained load.
The tiny benchmark app (Ractor-shareable, so Kino runs it in :ractor
or :threaded). Kino is ~7× lighter in :ractor mode, ~10× in :threaded
than the Puma cluster — the gap stays large because a trivial app is almost
all private per-worker heap, which copy-on-write can't share:
A real Rails app (not Ractor-shareable—Kino's :threaded fallback
only, below). The gap is ~4×, smaller because Rails' large
framework is shared copy-on-write across Puma's forks: