projectsaturnstudios / rocketship
Drop-in replacement for Laravel Octane, with a native server on epoll/kqueue: no Swoole, no Go binary.
Package info
github.com/projectsaturnstudios/rocketship
pkg:composer/projectsaturnstudios/rocketship
Requires
- php: ~8.4.27 || ^8.5.12
- laminas/laminas-diactoros: ^3.0
- laravel/framework: ^10.10.1|^11.0|^12.0|^13.0
- laravel/prompts: ^0.1.24|^0.2.0|^0.3.0
- laravel/serializable-closure: ^1.3|^2.0
- nesbot/carbon: ^2.66.0|^3.0
- symfony/console: ^6.0|^7.0|^8.0
- symfony/psr-http-message-bridge: ^2.2.0|^6.4|^7.0|^8.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.6.1
- inertiajs/inertia-laravel: ^1.3.2|^2.0
- laravel/scout: ^10.2.1
- laravel/socialite: ^5.6.1
- livewire/livewire: ^2.12.3|^3.0
- nunomaduro/collision: ^6.4.0|^7.5.2|^8.0
- orchestra/testbench: ^8.21|^9.0|^10.0|^11.0
- pestphp/pest: ^4.0
- phpstan/phpstan: ^2.1.7
- phpunit/phpunit: ^10.4|^11.5|^12.0|^13.0
- spiral/roadrunner-cli: ^2.6.0
- spiral/roadrunner-http: ^3.3.0
Suggests
- ext-epoll: Native server on Linux: pie install php-io-extensions/epoll
- ext-kqueue: Native server on macOS: pie install php-io-extensions/kqueue
- ext-parallel: Native server threads mode, on a ZTS PHP.
- ext-pcntl: Native server: supervises its workers. Required by the native server in both modes.
- ext-posi: Native server: pie install php-io-extensions/posi (0.10.6 or later). Linux needs it in both modes; threads mode needs it everywhere.
- ext-posix: Native server: required in both modes.
- ext-swoole: Octane's Swoole server.
Provides
None
Conflicts
- laravel/octane: *
- spiral/roadrunner: <2023.1.0
- spiral/roadrunner-cli: <2.6.0
- spiral/roadrunner-http: <3.3.0
Replaces
None
This package is auto-updated.
Last update: 2026-10-23 05:45:12 UTC
README
ProjectSaturn Rocketship
A drop-in replacement for Laravel Octane, with a server written in PHP
Laravel Octane boots your app once and keeps it in memory. To do that it needs an application server from outside PHP: Swoole, a C extension that brings its own coroutine runtime; RoadRunner, a Go binary that talks to PHP workers over pipes; or FrankenPHP, a Go binary with PHP built in.
PHP can serve HTTP itself if it can ask the kernel which sockets are ready. Linux answers that with epoll, macOS with kqueue. PHP never exposed either one to userland, so I wrote ext-epoll, ext-kqueue and ext-posi: the syscalls, one to one, no opinions.
Rocketship is Octane with a fourth server, native, built on those extensions. It's an HTTP/1.1 server written in PHP. Its workers are forked processes on any PHP, or threads on a ZTS PHP with ext-parallel. It runs under Octane's own commands, config and events.
Everything else is Octane's own code, forked at v2.22.0 under its MIT license: the Laravel\Octane namespace, config/octane.php, the Octane facade, the listeners, and the Swoole, RoadRunner and FrankenPHP servers. Your app code doesn't change. You swap the package and choose a server.
Benchmarks
Measured on a 2 vCPU Graviton2 box, with the app, nginx and MySQL sharing it and the load sent from a second machine, against Octane's three servers with Octane's defaults:
- PHP 8.4 ZTS, native in threads mode: 9–20% more requests per second than Swoole, 13–24% more than FrankenPHP, and 61–98% more than RoadRunner, on every route and load measured.
- 8–17% less server CPU per request than whichever of Octane's servers used the least on each route and load.
- 124 MB for the whole server, against 189 MB for FrankenPHP, 233 MB for RoadRunner and 315 MB for Swoole.
- PHP 8.4 NTS, native in processes mode: 11–17% more requests per second than Swoole on the plain, JSON and Blade routes, and 2–8% more on the MySQL route.
Where it doesn't win: at 256 connections native's slowest 1% of requests take longer than Swoole's and FrankenPHP's, and FrankenPHP has the shortest tail at every load.
The details are in BENCHMARKS.md: the box, every version, the method, every table, the raw data, the harness to rerun it, and the numbers that went the other way. Open the interactive report: charts for every round, route, load and metric, with inputs to filter them. It's one self-contained file, benchmarks/report.html, that also opens offline from a clone.
Installation
| PHP | 8.4.27 or newer on 8.4, 8.5.12 or newer on 8.5. NTS or ZTS. |
| Laravel | 10, 11, 12 or 13, the same as Octane 2.22. |
| Octane's servers | As in Octane: ext-swoole for Swoole, and the binaries octane:install fetches for RoadRunner and FrankenPHP. |
| Native, both modes | ext-pcntl and ext-posix. ext-epoll on Linux or ext-kqueue on macOS. On Linux, ext-posi 0.10.6 or newer. |
| Native, threads mode | A ZTS PHP with ext-parallel and ext-posi, without ext-swoole loaded in the same PHP. |
composer require projectsaturnstudios/rocketship
Then install the extensions for the native server with PIE, on the machine that runs it:
sudo pie install php-io-extensions/epoll # Linux pie install php-io-extensions/kqueue # macOS sudo pie install php-io-extensions/posi
For threads mode, also install ext-parallel on a ZTS PHP.
How to use
Migrating from Octane
composer remove laravel/octane composer require projectsaturnstudios/rocketship
Rocketship declares a conflict with laravel/octane, so remove Octane first. Everything you had keeps working as it was:
- Your code: the namespace is still
Laravel\Octane. TheOctanefacade,Octane::concurrently(),Octane::table(), ticks, theoctanecache store and Octane's events all resolve as before. config/octane.php: Rocketship reads the file you published. It adds onenativesection, and the defaults apply until you publish it again..env:OCTANE_SERVER,OCTANE_HTTPSand the otherOCTANE_*keys are read as before.- Your server: Swoole, RoadRunner and FrankenPHP run Octane's own code. A FrankenPHP worker file that Octane published is pointed at Rocketship's worker.
- Your process manager:
octane:start,octane:stop,octane:reloadandoctane:statuskeep their names and options.
The native server
php artisan octane:install --server=native php artisan octane:start --server=native
octane:install --server=native checks this PHP's extensions and prints what each mode is missing. octane:start takes Octane's options: --host, --port, --workers (default: CPU count), --max-requests (default: 500), --watch and --poll. Set OCTANE_SERVER=native to make it the default.
Native picks its mode when it starts. auto, the default, chooses threads when this PHP can run them and processes when it can't. To force one:
OCTANE_NATIVE_MODE=processes php artisan octane:start --server=native php artisan octane:native --mode=threads
octane:native is the command that octane:start --server=native runs, as octane:swoole is for Swoole.
What native does:
- HTTP/1.1 with keep-alive, pipelining, chunked bodies,
100 Continue, streamed responses and multipart uploads. - TLS, when you give it a certificate.
- Static files from
public/, with ETag,Last-Modified,304and byte ranges. max_requests, andmax_execution_timewith a408and a fresh worker, as on Swoole.octane:reloadwithout dropping a request,octane:stopwith a drain, and a replacement for a worker that dies.
What it doesn't do yet: native has no task workers, ticks or Swoole tables. Octane::concurrently() runs its tasks in the request, ticks don't fire, and Octane::table() and the octane cache behave as they do on RoadRunner and FrankenPHP. There's no HTTP/2.
The native section of config/octane.php:
| Key | Default | |
|---|---|---|
mode |
env('OCTANE_NATIVE_MODE', 'auto') |
auto, threads or processes. |
tls.cert, tls.key |
OCTANE_NATIVE_TLS_CERT, OCTANE_NATIVE_TLS_KEY |
PEM paths. The key can live in the certificate file. |
drain_timeout |
30 |
Seconds a stop or reload waits for requests in flight. |
keepalive_timeout |
5 |
Seconds an idle keep-alive connection stays open. |
header_timeout |
30 |
Seconds a client has to send a request head, answered 408 after. |
max_body |
10485760 |
Largest request body in bytes, answered 413 past it. |
php_options |
[] |
Extra arguments for the PHP that runs the server, such as -d settings. |
Put nginx or another proxy in front in production, as with Octane's other servers.
Adding your own server
Rocketship keeps its servers in a registry, so a package can add one. Implement ProjectSaturnStudios\Rocketship\Servers\ServerDriver: start, stop, reload, running and install, each handed the Octane command that was run. Then register it from any service provider:
use Laravel\Octane\Facades\Octane; public function register(): void { Octane::registerServer('myserver', MyServer::class); }
After that, octane:start --server=myserver and every other Octane command run through it, and octane:install offers it in its list. A later registration with the same name replaces an earlier one, built-ins included.
AI-assisted development
This codebase was built with LLM assistance under human direction. Prompts, reviews and architectural decisions were owned by a professional software engineer. The model speeds up drafting and exploration; it doesn't replace judgment on APIs, dependency direction or what ships.
Running the test suite
The suite is Pest v4, in two parts that run in separate processes: Octane's own tests, and Rocketship's tests for the registry, the event loop, the HTTP parser and the native server. The native server tests start real servers on free ports and talk to them over TCP.
git clone https://github.com/projectsaturnstudios/rocketship.git cd rocketship composer install vendor/bin/pest # Octane's tests vendor/bin/pest --testsuite=Rocketship # Rocketship's tests
composer test runs both.
Native tests need the extensions for the mode they cover. Without them, they're skipped. Threads-mode tests run only on a ZTS PHP with ext-parallel, and Linux-only tests are skipped on macOS. CI runs the suite on PHP 8.4 and 8.5, NTS and ZTS, on Linux with epoll and on macOS with kqueue.
Resources
- Laravel Octane: the docs for everything Rocketship keeps.
- laravel/octane: the upstream that Rocketship forks.
- ext-epoll, ext-kqueue and ext-posi: the extensions the native server runs on.
- ext-parallel: the threads behind threads mode.
Contributing
Open an issue first for anything beyond a typo. A pull request comes with a Pest test for the behaviour it changes and keeps CI green. Code under rocketship/ and bin/native-* is Rocketship's. Everything else is Octane's, and changes there stay as close to upstream as the fix allows, so upstream fixes keep porting cleanly. The native server follows Swoole: where a behaviour question comes up, the answer is what Octane's Swoole server does.
Security
The native server is a network server: it parses HTTP from clients, serves files from public/ and terminates TLS when you give it a certificate. See SECURITY.md for the model and how to report a vulnerability.
License
MIT. See LICENSE.md. Rocketship is a fork of Laravel Octane by Taylor Otwell and the Laravel contributors, under the same license.