rubyd is a from-scratch Ruby HTTP server project designed for systems-style experimentation.
It uses low-level sockets (TCPServer, IO.select), a thread-pool worker model, a Ruby DSL config,
a plugin system, and a built-in benchmark client.
- Evented accept loop with
IO.select - Thread-pool request workers for concurrent clients
- CLI lifecycle management:
start,stop,restart,reload,status - PID file management via
rubyd.pid - Signal handling (
SIGINT,SIGTERM,SIGHUPwhen supported) - Ruby DSL configuration (
config.rb) - Plugin hooks (
before_request,after_response) + route registration - Access and error logging to files with rotation
- Built-in load tool:
rubyd bench - Automatic default website in
www/index.html - Keep-alive, graceful connection draining, and request middleware pipeline
- TLS listener support via Ruby OpenSSL
- WebSocket handshake + text echo loop
- Virtual hosts (host header to root mapping)
- Reverse proxy rules
- Rate limiting + IP allow/block lists + optional basic auth
- Caching (
ETag,Last-Modified,Cache-Control) + range requests - Compression (
gzip, optionalbrwhenbrotligem is present) - Upload endpoint (
/upload) with multipart handling - Metrics endpoint (
/metrics) - Directory listing + custom error pages (
www/errors/*.html) - Native
rubydtemplate pages (.rubyd)
rubyd/
├── bin/rubyd
├── lib/rubyd/
│ ├── server.rb
│ ├── parser.rb
│ ├── router.rb
│ ├── config.rb
│ ├── logger.rb
│ ├── plugin.rb
│ ├── cli.rb
│ ├── bench.rb
├── plugins/
├── www/
├── logs/
├── rubyd.pid
├── config.rb
├── README.md
From the rubyd directory:
ruby bin/rubyd startThen check status:
ruby bin/rubyd statusStop server:
ruby bin/rubyd stopruby bin/rubyd start- start in backgroundruby bin/rubyd stop- stop gracefully (TERM)ruby bin/rubyd restart- stop + startruby bin/rubyd reload- trigger config reload (HUP)ruby bin/rubyd status- process status from PID fileruby bin/rubyd bench- run benchmark against active server
ruby bin/rubyd bench -n 10000 -c 50
ruby bin/rubyd bench -n 5000 -c 20 --path /echoExample output:
Requests: 10000
Concurrency: 50
Completed: 10000
Failed: 0
RPS: 3200.12
Avg Latency: 12.34ms
Max Latency: 38.91ms
port 9292
host "0.0.0.0"
root "www"
worker_threads 8
plugins do
enable :echo
# disable :echo
endPlugins live in plugins/*.rb and should:
- Subclass
Rubyd::Plugin::Base - Optionally implement hooks:
setup(router)before_request(request)after_response(request, response)
- Register with
Rubyd::Plugin.register(:name, PluginClass)
See plugins/echo.rb for a working example.
rubyd includes a built-in PHP-like runtime (no external php-cgi required).
Supported today in native mode:
<?php ... ?>and<?= ... ?>echo ...;- variable assignment (
$name = ...;) $_GET[...],$_POST[...], and$_SERVER[...]- null-coalescing (
??), concatenation (.), and comparisons (==,!=,>,<,>=,<=) header(...)gmdate(...)andhtmlspecialchars(...)include,include_onceif (...) { ... } else { ... }foreach (...) { ... }function name(...) { ... }+return
Examples:
ruby bin/rubyd start
# then open http://127.0.0.1:9292/info.php?name=rubydFiles with .rubyd are rendered as HTML with simple placeholders:
{{method}}{{path}}{{query}}{{remote_addr}}{{time}}
Example:
# open http://127.0.0.1:9292/example.rubyd?demo=1GET /healthbasic health probeGET /metricsJSON runtime metricsGET /uploadupload formPOST /uploadmultipart upload target