RoadRunner
Why RoadRunner
PHP-FPM boots the framework on every request. RoadRunner keeps workers alive — boot once, handle many requests. ConnectionManager reuses PDO across requests; call disconnect() only on worker shutdown.
Setup
composer require spiral/roadrunner-http nyholm/psr7
composer require --dev spiral/roadrunner-cli
php pionia rr:setup
php pionia runserverFor production deploy, run php pionia optimize --production and configure OPcache preload — see Production performance.
HTTP listen address
Resolved in order:
- CLI
--port/--host PORT/SERVER_PORTin.env[roadrunner]or[server]insettings.ini.rr.yaml→http.address- Default 8003 (same as
php pionia serveand the frontend API proxy)
runserver passes -o http.address=… when the resolved address differs from the file.
Commands
| Command | Purpose |
|---|---|
php pionia runserver | Foreground RR (formatted access logs in terminal) |
php pionia runserver --detach | Background; logs to storage/logs/roadrunner.log |
php pionia runserver:logs | Tail log file (Ctrl+C to stop) |
php pionia stopserver | Stop detached instance |
Log formatting
RoadRunner emits structured HTTP lines. Pionia formats them for readability:
13:18:42 GET /api/v1/ping 200 1.2 KB 4msUse --raw on runserver or runserver:logs for the original JSON lines.
runserver:logs options: --lines=50, --no-follow, --wait (block until log exists), --log=/path.
Worker entry
worker.php → PioniaWorker routes by RR_MODE:
| Mode | Handler |
|---|---|
http | HTTP request loop |
jobs | Moonlight job consumer |
centrifuge | WebSocket RPC (optional) |
Never call exit() in route handlers — it kills the worker process.
Use handleRequest() / worker-safe patterns from request lifecycle.
Moonlight jobs
environment/settings.ini:
[jobs]
ENABLED = true
PIPELINE = moonlight
RPC = tcp://127.0.0.1:6001.rr.yaml in your app root needs rpc + jobs sections when using background jobs.
moonlight()->async('mail', 'send_welcome', ['email' => $user->email]);Returns returnCode: 202 with job_id when the queue accepts the job. See Background work.
Realtime (WebSockets, optional)
RoadRunner can host a Centrifugo plugin for WebSocket RPC using the same Moonlight { service, action } envelope as HTTP.
composer require roadrunner-php/centrifugo- Uncomment the
centrifugesection in.rr.yaml - Enable in
environment/settings.ini:
[realtime]
ENABLED = true
CHANNEL_PREFIX = moonlightFrames are handled by MoonlightFrameHandler — responses match the HTTP JSON shape. This is optional; most apps only need HTTP + jobs.
Built-in dev server vs RR
php pionia serve | php pionia runserver | |
|---|---|---|
| Server | PHP -S | RoadRunner |
| Workers | New process per request | Persistent pool |
| Jobs queue | Sync fallback after response | Full RR jobs |
| Best for | Quick local API checks | Production-like testing |
Config reference
See .rr.yaml in your project root for HTTP pool, jobs pipeline, and optional Centrifugo block.
Related: Maintenance mode · Database connections · CLI.