Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Roadrunner Bundle Laravel Package

baldinof/roadrunner-bundle

Symfony bundle that integrates the RoadRunner high-performance PHP application server, providing an easily configurable, extendable worker with middleware support, kernel reboots, and out-of-the-box integrations for Sentry, sessions, Doctrine, Blackfire, and Xdebug.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • This is a Symfony bundle, not a Laravel package. It ships Baldinof\RoadRunnerBundle\BaldinofRoadRunnerBundle, a Symfony kernel bundle plus a Runtime entrypoint wired via APP_RUNTIME. It cannot be dropped into a Laravel application; Laravel's equivalent is first-party (laravel/octane with --server=roadrunner), which solves the same problem set (worker bootstrapping, per-request container reset, long-lived process safety).
  • Where the org runs Symfony, fit is strong: it turns a standard front-controller app into a persistent worker with the kernel and DI container reused across requests, without rewriting controllers. That is the core value proposition — removing per-request bootstrap cost.
  • Where the org is Laravel-only, this package's leverage is architectural reference, not a dependency. Its design decisions (kernel reboot strategies, ResetInterface on stateful services, integration shims for Doctrine/Sentry/sessions) map almost 1:1 onto Octane's sandbox and listener model, making it a useful checklist for what Octane must be validated against.
  • The bundle is the PHP-side half of the stack; the process manager, HTTP server, load balancer, and reload semantics live in the RoadRunner Go binary and its .rr.yaml. Any evaluation must treat the bundle + binary + config as one deployable unit.

Integration feasibility

  • Low friction on a Symfony app: composer require, optional Flex recipe, copy .rr.* configs, pull the binary with spiral/roadrunner-cli, run bin/rr serve. No application code changes required to boot; correctness work begins at state hygiene.
  • Auto-detected integrations reduce bespoke glue: Sentry request context, session cookie attachment, Doctrine ORM connection revalidation and manager clearing, Doctrine MongoDB manager clearing, Blackfire probe, Xdebug trigger mode. Each is conditional on the companion bundle/extension being present, so the blast radius of adoption is roughly "whatever you already have installed."
  • Requires a PSR-7/PSR-17 implementation in the container (nyholm/psr7 or diactoros, typically via symfony/psr-http-message-bridge); changelog shows a DI misconfiguration when sensio/framework-extra-bundle is present without nyholm/psr7. This is a known sharp edge worth pre-empting in the base image.
  • Middlewares are supported but are explicitly outside Kernel::handle() and are resolved once at worker start. That constrains their use to transport/response-shaping concerns, not application logic, and makes initialization cost a startup-time concern rather than per-request.

Technical risk

  • Version drift with the RoadRunner binary is the top risk. The bundle's documented surface targets RoadRunner 2.x (2.0.0 release notes), while current RoadRunner releases use year-based versioning. rr get pulls latest by default, so an unpinned binary can silently outrun the bundle's relay/RPC protocol assumptions. Mitigation: pin the exact binary version in the Docker image and CI, and treat binary upgrades as bundle upgrades.
  • Cross-request state leakage is the failure class you inherit. The changelog is a catalogue of exactly these bugs: kernel not properly reset (1.2.0), Sentry scope bleed (1.3.2), session not closed on handler exception (1.0.1), uploaded files invalid (2.0.1), StreamedResponseListener firing early (1.2.2), Basic Auth server vars unpopulated (1.0.2). Each was fixed, but the pattern tells you the regression surface is ongoing, not closed.
  • Single-maintainer project with low visible adoption (309 stars, 0 dependents recorded, MIT). The project is healthy in hygiene (README, changelog, tagged releases, recent release) but thin in bus factor. There is no vendor SLA; support is best-effort GitHub issues.
  • Kernel reboot semantics are a config-level correctness decision, not a performance knob alone. on_exception with an allowed_exceptions allowlist is the default-efficiency path but requires the team to correctly classify which exceptions leave services in a recoverable state. Misclassification produces intermittent, hard-to-reproduce corruption in long-lived workers.
  • Stateful services are the team's responsibility. ResetInterface must be implemented on anything holding per-request state; the bundle will not detect omissions.
  • Observability gaps are addressable in-bundle (Prometheus metrics endpoint, RPC listener, four lifecycle events), but "addressable" is not "instrumented" — someone must build dashboards and alerts.

Key questions

  1. Is the target application Symfony or Laravel? If Laravel, this is a reference design and the real decision is Octane + RoadRunner; if Symfony, this is a direct adoption candidate.
  2. Which Symfony and Doctrine versions are in play? Verify support matrix against the app's composer.lock, especially DBAL 3.x (fixed in 2.1.1) and Symfony 6 (2.1.2).
  3. Is the RoadRunner binary version pinned, and does the chosen bundle version support it? Run a spike before committing.
  4. Which services in the app are stateful, and how many already implement ResetInterface? This is the single largest unestimated work item.
  5. What exception taxonomy does the app have, and can we safely allowlist framework/serializer/domain exceptions to avoid full container rebuilds on every 4xx/5xx?
  6. Is there appetite to own a Go binary in the deployment pipeline (image build, CVE scanning, upgrade cadence)?
  7. What is the quantified baseline — p95 latency, requests/sec/core, memory ceiling — that justifies the change? Without a before/after, this is an unprovable win.

Integration Approach

Stack fit

  • Symfony app served today by PHP-FPM + Nginx is the natural candidate. The bundle replaces the FPM request lifecycle with a persistent worker; Nginx is typically retired or demoted to TLS termination in front of RoadRunner's HTTP server.
  • Complementary capabilities expand the value beyond raw throughput: gRPC service auto-registration (proto-generated interfaces resolved from the container), KV storage exposed as Symfony cache adapters (cache.adapter.roadrunner.kv_<name>), and Prometheus metrics declared in Symfony config and scraped from the RoadRunner endpoint. These are the levers that make the migration a platform change rather than a server swap.
  • Poor fit for: apps with heavy per-request container mutation, apps relying on shutdown-function semantics, apps with third-party code holding static state, and teams without the bandwidth to own a Go binary.

Migration path

  • Phased, non-destructive, and reversible by construction: FPM and RoadRunner can run side-by-side behind a load balancer during cutover, which keeps rollback to a routing change.
  • Recommended progression: (1) read-only spike on a low-traffic internal service; (2) add the dependency and .rr.yaml to a single bounded service in dev, exercise the dev config (.rr.dev.yaml auto-reload); (3) fix state hygiene findings; (4) canary in production behind weighted routing; (5) widen by service.
  • Do not start with the highest-traffic or most stateful service. The first adoption should be chosen for low blast radius and clean state, so the team learns the failure modes cheaply.
  • Treat the RoadRunner binary as an artifact to be version-pinned and baked into images — mirror the Dockerfile example's approach (rr get-binary --location /usr/local/bin) but with an explicit version flag rather than latest.

Compatibility

  • Documented support spans Symfony 4.4 through 6.x and RoadRunner 2.x; DBAL 3.x and Symfony 6 sessions are handled. Confirm current production versions against this envelope before sequencing.
  • PSR-7/PSR-17 factory must be present; prefer nyholm/psr7 given the documented conflict with sensio/framework-extra-bundle when it is absent.
  • Dev-mode parity matters: the .rr.dev.yaml reload config and VarDumper caveat (dumps go to bin/console server:dump or the profiler, not the response body) should be written into the onboarding doc, or the first sprint will be spent rediscovering it.
  • Doctrine ORM and MongoDB integrations are automatic, but "clears managers and checks connection" is not the same as "your transactions are safe." Validate transactional boundaries under concurrency in the spike.

Sequencing

  1. Spike (1–2 weeks, 1 engineer): stand up one service under RoadRunner, pin the binary, run the existing test suite plus a load test, and enumerate stateful services and their ResetInterface status. Deliverable: go/no-go with measured baseline deltas.
  2. Risk burn-down: implement ResetInterface where missing; classify exceptions into the allowed_exceptions allowlist; decide the reboot strategy (start conservative with always or on_exception with a narrow allowlist, then relax).
  3. Platform plumbing: CI step for the pinned binary, container image update, Prometheus scrape config, lifecycle-event hooks into existing logging/alerting.
  4. Canary: weighted routing to RoadRunner, shadow-compare latency/error rate/memory against FPM, with a defined abort threshold.
  5. Widen and decommission: migrate service-by-service; retire FPM configs only after a full release cycle at target traffic.
  6. Parallel track, if Laravel is in scope: map each of the above onto Octane's sandbox model and reuse the state-hygiene inventory rather than repeating the discovery.

Operational Impact

Maintenance

  • Two moving parts to keep current: the Composer package and the RoadRunner Go binary. Neither is high-churn, but they are version-coupled and must be upgraded together — an operational discipline the team must adopt explicitly.
  • Bundle hygiene is good (semantic versioning, per-release changelog files, recent releases). Single-maintainer risk means the realistic contingency is forking — MIT license makes that viable, and the codebase is small enough that a fork is a days-not-months proposition. Budget for that possibility rather than assuming vendor support.
  • Historical fix cadence suggests roughly steady small patches for integration edge cases rather than large breaking changes; the 2.0 release was the notable breaking boundary (PSR middleware removal, RoadRunner 2.x requirement).

Support

  • No commercial support, no SLA. Escalation path is GitHub issues plus internal expertise. Recommend designating one engineer as owner with a documented runbook, and keeping the FPM deployment warm as a fallback for at least one release cycle.
  • The lifecycle events (WorkerStartEvent, WorkerStopEvent, WorkerExceptionEvent, WorkerKernelRebootedEvent) are the supported extension points for operational hooks — use them for metrics, log correlation, and alerting rather than patching internals (Worker is marked internal as of 1.3.0).

Scaling

  • Scaling model changes from "add PHP-FPM processes" to "add RoadRunner workers," with the Go server handling accept loops, load balancing, and process supervision. Per-worker memory footprint rises because the kernel and container persist; plan worker count against measured steady-state RSS, not FPM's per-request ceiling.
  • The memory reboot strategy (threshold in MB, default 128) is the built-in guard against slow leaks; max_jobs (with max_jobs_dispersion, e.g. 0.2, to avoid synchronized restarts) is the guard against cumulative drift. Combining [on_exception, max_jobs, memory] is the recommended production posture for a first cut — it trades a small amount of throughput for predictability.
  • Autoscaling should key on RoadRunner/Prometheus metrics (worker count, request rate, memory) rather than CPU alone, since CPU utilization under a persistent-worker model is a lagging and flatter signal.
  • gRPC and KV plugins are optional scaling surfaces: gRPC for service-to-service if the org uses it, KV to offload hot cache reads from Redis — but each adds a plugin to configure and monitor. Adopt only where there is a measured need.

Failure modes

  • State corruption across requests — highest severity, lowest observability. Manifests as intermittent wrong data for the wrong user, typically after an exception path that skipped reset. Mitigation: conservative reboot strategy initially, ResetInterface audit, and integration tests that deliberately throw and then assert clean state on the next request.
  • Memory exhaustion — long-lived workers accumulate; memory strategy plus container memory limits plus OOM alerting.
  • Stale database connections — the Doctrine integration checks and reconnects, but poolers/proxies (PgBouncer, ProxySQL) can still terminate idle connections; validate against the actual topology.
  • Binary/package version skew — worker fails to boot or relay protocol mismatch after an unpinned rr get. Mitigation: pin, and fail the build on mismatch.
  • Silent observability loss — Sentry scope bleed and VarDumper suppression are documented past incidents; verify error reporting end-to-end under RoadRunner before canary, not after.
  • Restart storms — if max_jobs_dispersion is omitted, all workers reboot together and capacity momentarily collapses; always set dispersion.
  • Rollback is genuinely cheap (route traffic back to FPM), which caps the downside of any of the above — provided the FPM path is kept deployable through the transition.

Ramp-up

  • Engineer learning curve is moderate: the Symfony-side config is small, but reasoning about persistent-process lifecycle, reset semantics, and binary versioning is new muscle for a typical FPM team. Estimate 1–2 weeks to productive, 1 quarter to genuinely fluent across the team.
  • Highest-value onboarding artifacts, in order: (1) an inventory of stateful services and their reset status, (2) a written reboot-strategy decision with rationale, (3) the dev-mode workflow including the VarDumper caveat, (4) a runbook covering binary upgrade, memory alarms, and FPM fallback.
  • For a Laravel org, ramp-up cost is near zero for this package (it will not be installed) but transfers almost entirely to Octane: the same inventory, the same reset discipline, the same observability validation. Framing the effort that way avoids paying for the discovery twice.
  • Overall TPM recommendation: proceed to a bounded spike if Symfony is in the estate; treat as a reference architecture and route to Octane if Laravel-only. The decision gate should be a measured latency/throughput/memory delta plus a completed stateful-service inventory — not the README's feature list.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk