roadrunner/worker
Core PHP worker package for RoadRunner that provides the Worker API, payload handling, and stdin/stdout communication with the RoadRunner server. Use it to build custom long-running workers and integrate them with RoadRunner's high-performance Go runtime.
What changed since the last assessment. The picture has moved from a single release to a short, active release cadence. v3.8.0 (2026-10-10) was the material one: the formal rename to roadrunner/worker, the PHP 8.2 floor, refreshed docs, and inert #[\Override] annotations. v3.8.2 (2026-10-19) — with a v3.8.1 in between that we have not reviewed — is a single-line bug fix: the worker now detects the RoadRunner version from the roadrunner/roadrunner package rather than from whatever source it used previously. That is a small change with a non-trivial implication: version detection has moved into the Composer dependency graph, which is a different trust boundary from the rr binary we actually ship in the image. The #[\Override] and docs changes remain cosmetic. Nothing here is a breaking change, and nothing alters the strategic call — but it sharpens the version-skew story and is worth folding into the runbook and CI checks.
Architecture fit. Unchanged in substance, now confirmed in both name and organisation. roadrunner/worker is not an application-level library — it is the PHP-side runtime that speaks the Goridge protocol to the RoadRunner (Go) server and drives the worker loop, turning a PHP script into a long-lived, request-consuming process. For a Laravel estate this is an implementation detail of the RoadRunner adapter, not something application code should reference. The architecture question remains indirect: do we want a persistent-worker execution model (RoadRunner/Octane/FrankenPHP) at all, and is RoadRunner the runtime we standardise on? The v3.8.2 fix, which reaches into a sibling roadrunner/* package for the server version, reinforces that this is infrastructure plumbing — the package now has opinions about its own deploy environment.
Integration feasibility. Feasible but indirect, and slightly more constrained than before. Laravel teams reach RoadRunner via Octane's RoadRunner driver or spiral/roadrunner-laravel/bridge packages, which pull this worker transitively. The rename introduces a genuine Composer identity problem: any bridge package that still requires spiral/roadrunner-worker will either conflict with, or install alongside, the new name — risking a duplicate vendor tree or an unresolvable dependency set. This must be resolved by the bridge maintainers, not by us. Added by v3.8.2: the worker now reads the server version from roadrunner/roadrunner, so we should establish whether that package is a genuine installable artifact (does it ship or pin the rr binary?) or merely version metadata. Either way it becomes a fifth thing to keep in lockstep, and one that Composer can silently satisfy with a version different from the binary on disk. Direct adoption still only makes sense if we are writing a custom worker bootstrap, which should be an explicit architecture decision. The package remains framework-agnostic (PSR-7/PSR-17 oriented), MIT-licensed, and small — the integration cost sits in the Go binary, the worker lifecycle, and now the version-detection contract.
Technical risk.
composer update — or worse, resolves to an older pinned version and drifts from the rest of the fleet. Verify the floor against our actual minimum-supported PHP and against the Laravel version matrix before assuming this is free.spiral/roadrunner-worker will need updating, otherwise the package drops out of our supply-chain visibility. v3.8.2 widens this: there are now at least three roadrunner/* names in play (roadrunner/worker, roadrunner/roadrunner, plus the legacy spiral/* name), so scanner rules should match on the vendor namespace, not a single package string.rr process. If roadrunner/roadrunner resolves to version X but the base image ships binary Y, the worker's compatibility assumptions are wrong in a way that is invisible until a protocol feature is used. Treat the package version and the binary version as a paired pin, verified in CI.rr binary alongside PHP adds a second versioned artifact to every image, with a protocol compatibility contract between binary and PHP worker — now mediated by a third artifact's declared version.Key questions.
spiral/roadrunner-worker?vendor/?roadrunner/roadrunner package version, the actual rr binary version, and Laravel Octane's supported versions — and which of the four is authoritative when they disagree?roadrunner/roadrunner ship or pin the binary, or is it metadata only? If metadata, how do we guarantee the declared version matches the binary baked into the base image?Stack fit. RoadRunner targets throughput-sensitive, IO-bound HTTP APIs and queue-style services. It is a poor fit for teams relying on per-request globals, register_shutdown_function semantics, or extensions that assume process-per-request teardown. Laravel support via Octane is mature enough that the framework is not the constraint — the constraint is our application's hidden statefulness, plus our PHP version floor and, as of v3.8.2, how faithfully we pin the binary-to-package version pair.
Migration path. Do not adopt this package directly. Adopt it via Laravel Octane's RoadRunner driver (or the Spiral Laravel bridge), pin the transitive version explicitly in composer.lock, and treat the rr binary as a first-class pinned dependency in the base image. Treat the rename as a distinct, scheduled change rather than something that arrives with a routine composer update. Sequence it as (a) confirm bridge support for roadrunner/worker, (b) update internal allowlists, SBOM tooling and docs to the new vendor namespace, (c) verify only one of the two legacy names resolves into vendor/, (d) roll out with the base image, not per-service. Added for v3.8.2: pin roadrunner/roadrunner alongside the binary and assert in CI that the version the worker detects equals the version of the rr binary actually invoked.
Compatibility. Five axes must now be locked together: PHP minor (≥8.2), package name and version (roadrunner/worker vs. legacy spiral/roadrunner-worker), the bridge/Octane version, the roadrunner/roadrunner version used for detection, and the rr binary version. Add a CI check that boots the worker against the pinned binary and issues a real request — this catches protocol skew, duplicate-package resolution, and (new) version-detection mismatch before deploy. Consider having that check log the detected version and diff it against the binary's --version output. Confirm the PSR-7 implementation chosen by the bridge is consistent across services.
Sequencing.
rr.yaml; pin the binary and roadrunner/roadrunner together; make direct dependencies on the worker package a lint rule; update internal naming/SBOM references to the vendor namespace.Maintenance. Low code-churn surface (a thin protocol/loop layer), but maintenance burden shifts to image and configuration management: the rr binary, rr.yaml, worker pool sizing, and PHP extension parity. The v3.8.2 release nudges a little of that burden back into Composer: the base image now has a declared-version package and a separate on-disk binary that must agree. Add a one-time housekeeping cost to retire the spiral/roadrunner-worker name from internal tooling, scanner exceptions, and developer documentation. Budget ongoing ownership for the base image and the version-pin pair, not for this library.
Support. No commercial support implied by an MIT package. Our support path is the Octane/bridge maintainers and the RoadRunner community channels. Given the low visibility signals, assume we are effectively self-supporting for edge cases and plan an internal owner (platform team) able to read the worker loop source — it is small enough that this is realistic. The rename and the recent cadence raise the value of that internal owner: we are more likely than the community to notice a broken bridge constraint or a detection mismatch first, because our install shapes differ from the maintainers'.
Scaling. RoadRunner scales well horizontally (more pods) and vertically (more workers per pod), but worker count must be tuned against memory: each worker is a full Laravel bootstrap. Set max_jobs and memory limits explicitly to force recycling before leaks accumulate. Autoscaling metrics change — CPU per pod rises while concurrency is handled in-process; HPA thresholds tuned for php-fpm will mis-fire.
Failure modes.
roadrunner/roadrunner resolves to a version that does not match the rr binary in the image, so the worker makes incorrect protocol assumptions. Manifests as intermittent, hard-to-attribute failures on specific features rather than a clean boot error — the worst kind of skew. Caught by asserting detected-vs-actual in CI.roadrunner/* namespace and fix at rename time, not later.rr and the PHP worker → worker fails to boot or hangs; surfaces as ingress 502/504 with little PHP-side logging.max_jobs and soak testing, detected by per-worker RSS monitoring.$_SERVER-derived context, some profiling hooks, and per-request log correlation behave differently; validate tracing propagation early.Ramp-up. Two to three weeks of genuine learning per team: worker lifecycle semantics, Goridge/RoadRunner config, debugging across the Go/PHP boundary, and local development parity (rr serve vs. artisan serve). The package itself is learnable in an afternoon. These releases add negligible ramp-up but do add Composer troubleshooting wrinkles worth a paragraph each in the runbook — "why is there a spiral/roadrunner-worker directory in vendor/?" and "why does the worker think the server is a different version?" will both be asked. Invest in a runbook and golden-path base image before the second team onboards.
Recommendation. v3.8.2 is a patch, not a pivot: a one-line fix that relocates server-version detection into the Composer graph. It changes no strategic conclusion. Continue to treat roadrunner/worker as infrastructure consumed transitively, never as a direct application dependency, and continue to gate adoption on a soak-tested pilot. Two additions to the prerequisite list: (1) treat roadrunner/roadrunner and the rr binary as a paired pin, verified in CI rather than assumed; (2) review the unreviewed v3.8.1 before committing to a version. The rename remains the larger operational item — it is still the case that failing to update name-keyed tooling turns the rename into a supply-chain blind spot rather than a footnote, and the growing family of roadrunner/* packages makes namespace-level scanner rules the safer configuration.
How can I help you explore Laravel packages today?