spiral/roadrunner
High-performance PHP application server and process manager written in Go. RoadRunner replaces Nginx+FPM with long-running workers and a plugin system, offering HTTP(S)/2/3, FastCGI, PSR-7/17 support, and per-project extensibility.
Installation:
composer require spiral/roadrunner-cli
./vendor/bin/rr get-binary
Verify the binary exists in your project root.
Configure .rr.yaml:
version: '3'
server:
command: "php worker.php"
http:
address: "0.0.0.0:8080"
Create a Worker (worker.php):
<?php
use Spiral\RoadRunner;
use Nyholm\Psr7\Factory\Psr17Factory;
$worker = RoadRunner\Worker::create();
$psrFactory = new Psr17Factory();
$httpWorker = new RoadRunner\Http\PSR7Worker($worker, $psrFactory, $psrFactory, $psrFactory);
while ($req = $httpWorker->waitRequest()) {
$httpWorker->respond(new \Nyholm\Psr7\Response(200, [], 'Hello RoadRunner!'));
}
Run:
./rr serve -c .rr.yaml
Replace your Laravel index.php with the above worker pattern. RoadRunner handles routing via middleware/plugins, so integrate Laravel’s middleware stack into the PSR-7 worker.
Laravel Middleware: Wrap Laravel’s middleware in PSR-15 middleware and inject them into the PSR7Worker:
$middleware = new class implements Psr\Http\Server\MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
// Laravel middleware logic
return $handler->handle($request);
}
};
$httpWorker = new RoadRunner\Http\PSR7Worker($worker, $psrFactory, $psrFactory, $psrFactory, [$middleware]);
RoadRunner Middleware: Use built-in middleware (e.g., gzip, headers) in .rr.yaml:
http:
middleware: ["gzip", "headers"]
$jobWorker = new RoadRunner\Job\JobWorker($worker);
while ($job = $jobWorker->wait()) {
$job->perform(); // Laravel job logic
$job->ack();
}
.rr.yaml:
jobs:
pool:
num_workers: 4
queue: redis://127.0.0.1:6379
.proto files and generate PHP classes. Use RoadRunner’s gRPC plugin:
grpc:
proto: "app/proto/*.proto"
$grpcWorker = new RoadRunner\Grpc\GrpcWorker($worker, new MyGrpcService());
$app->singleton(RoadRunner\Worker::class, function () {
return RoadRunner\Worker::create();
});
SIGUSR2 for graceful reloads (requires .rr.yaml symlink):
server:
reload:
enabled: true
PHP Extensions:
php-sockets is installed (php --modules). Missing it causes EOF errors.net package; ensure your PHP process has socket permissions.Configuration Overrides:
.rr.yaml (e.g., env: APP_ENV=${APP_ENV}) are not interpolated by default. Use rr serve --env APP_ENV=local or a .env loader plugin.Worker Isolation:
jobs plugin or worker_pool:
worker_pool:
num_workers: 8
TLS Management:
.rr.yaml:
http:
tls:
cert: "/path/to/cert.pem"
key: "/path/to/key.pem"
Plugin Conflicts:
fileserver and http plugins for the same port. Use port: 8080 and fileserver_port: 8081.Logs:
logs.level: debug in .rr.yaml for verbose output.rr logs to stream logs in real-time.Metrics:
http:
middleware: ["prometheus"]
http://localhost:8080/metrics.Worker Crashes:
rr logs --level debug). Common causes:
try-catch).php-curl for HTTP plugins).Performance:
worker_pool.num_workers). Too many workers cause memory bloat; too few throttle requests.autoscaling for dynamic workloads:
worker_pool:
autoscaling:
min_workers: 2
max_workers: 16
Custom Plugins:
Middleware Chaining:
$middlewareStack = new \Laravel\Middleware\Pipeline($app);
$middlewareStack->send($request)->through([...]);
Telemetry:
otel:
service_name: "my-app"
exporter: "jaeger"
Systemd Integration:
systemd plugin for process management:
systemd:
enabled: true
service_name: "my-app"
Service Provider Bootstrapping:
$app = require __DIR__.'/bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
Request Lifecycle:
Request class won’t work directly. Use PSR-7 adapters:
use Illuminate\Http\Request;
$request = Request::createFromBase($psrRequest);
Queue Listeners:
$jobWorker = new RoadRunner\Job\JobWorker($worker);
while ($job = $jobWorker->wait()) {
$job->setPayload(json_decode($job->getPayload(), true));
app()->make(HandleJobs::class)->handle($job);
$job->ack();
}
Artisan Commands:
cli plugin:
cli:
command: "php artisan queue:work"
How can I help you explore Laravel packages today?