winter/wn-octane-plugin
Integrates Laravel Octane with Winter CMS so your site can run on Swoole or RoadRunner for faster responses and higher throughput. A lightweight plugin for developers looking to boost performance without changing how they build with Winter.
The Octane plugin lets a Winter CMS site boot once and serve many requests, instead of rebuilding the application on every hit. Install it after Octane itself is working, and pin the new patch release explicitly — v1.1.0 is unusable via Composer (see Gotchas).
composer require laravel/octane
php artisan octane:install # choose roadrunner or swoole
composer require wintercms/wn-octane-plugin:1.1.1
php artisan plugin:install Winter.Octane # or enable in Settings > Plugins
Then bring the site up on a non-production port and exercise it:
php artisan octane:start --server=roadrunner --host=127.0.0.1 --port=8090
Where to look first:
README and the v1.1.0 release notes (the 1.1.1 tag is a re-cut of the same code; the actual changes landed in #9 and #10 and are described there).config/octane.php (warm/tick arrays, listeners), since the plugin layers on top of it rather than replacing it.The first real use case to validate is a read-heavy front end (CMS pages, cached partials) behind RoadRunner in staging. Leave the backend/administrative areas on PHP-FPM until you have audited the plugins in use.
Start narrow, expand. Run the front-end site under Octane first. Admin, queue workers, and the scheduler should stay on the traditional runtime — they are separate processes (queue:work, schedule:work) and gain nothing from Octane.
Pin and verify the install. Because of the tag situation, confirm what you actually got:
composer show wintercms/wn-octane-plugin # expect 1.1.1
composer clear-cache
rm -rf vendor/wintercms/wn-octane-plugin
composer update wintercms/wn-octane-plugin --with-dependencies
Treat singletons as the enemy. Winter/October-era code leans on container singletons and static caches. Anything request-scoped that gets bound with App::singleton() will survive into the next request. Prefer App::scoped() for new bindings, and for third-party plugins register a reset in a listener on Octane's RequestTerminated (or the plugin's equivalent hook):
Event::listen(\Laravel\Octane\Events\RequestTerminated::class, function () {
MyPlugin\Models\Thing::flushStaticCaches();
\Winter\Storm\Support\Str::flushCache(); // if applicable
});
Deployment workflow. Do not git pull under a running server expecting a reload. Use:
php artisan down --render="errors::503"
php artisan octane:stop
composer install --no-dev --optimize-autoloader
rm -rf storage/framework/views/* # compiled CMS templates
php artisan octane:start --server=roadrunner --max-requests=500 --daemon
php artisan up
php artisan octane:reload is fine for config-free code changes; a full stop/start is safer when compiled templates or plugin migrations change.
Development loop. Use octane:start --watch so file changes restart workers, but remember --watch re-reads PHP only — CMS template and config changes still need the cache cleared.
Isolate state with middleware. For multi-site/multi-locale Winter installs, reset the active site, locale, and theme in middleware that runs early, rather than trusting the previous request to have cleaned up.
CI coverage. Run the test suite twice: once with the normal runtime and a smoke suite against a live Octane server. Octane-only bugs (leaked auth, stale Request data) never appear in the FPM test run.
composer require wintercms/wn-octane-plugin:1.1.0 installs code from before #9 and #10. Require 1.1.1 (or ^1.1.1). If a composer.lock already contains 1.1.0, editing the constraint alone is not enough — remove the entry or run composer update wintercms/wn-octane-plugin --with-dependencies, then verify with composer show.dd()/die()/exit() in Octane request code — you terminate the worker, not the request, and the client gets a broken connection.putenv() and superglobal mutation are unsafe under Swoole's coroutines. Prefer config() and $_SERVER reads over writes.config/octane.php points at a writable, per-worker-safe location.rr binary (install via the Octane installer); Swoole needs the PHP extension and behaves differently around coroutines and blocking I/O. Pick one and document it, since debugging advice rarely transfers.--max-requests (300–500 is a sane start) so workers recycle; watch RSS over an hour of real traffic before scaling down worker counts.opcache caveats. With opcache.validate_timestamps=0, deployed code changes require a worker reload — octane:reload exists precisely for this.WorkerStarting, RequestReceived, RequestTerminated, TaskReceived, TaskTerminated) for resets and metrics; Octane::tick() for periodic housekeeping, kept short and non-blocking; and octane:warm-style bootstrapping if the plugin exposes it, to pre-resolve the router, config, and CMS templates.static $, App::singleton, and $this->app->instance before enabling it under Octane.echo/var_dump output corrupts responses; use the Log facade and check storage/logs plus the Octane server's own log when diagnosing worker crashes.How can I help you explore Laravel packages today?