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

Octane Laravel Package

laravel/octane

Laravel Octane boosts your app's performance by serving it with high-powered servers like FrankenPHP, Open Swoole, Swoole, and RoadRunner. It boots your application once, keeps it in memory, and handles requests at supersonic speeds.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and pick a server in one step:

composer require laravel/octane
php artisan octane:install      # writes config/octane.php, picks frankenphp|swoole|roadrunner
php artisan octane:start --watch

Start by reading config/octane.php — it is the whole surface area of the package. Three blocks matter most day-to-day: server (frankenphp/roadrunner/swoole/openswoole), warm (services resolved once at boot instead of per request), and listeners (what gets flushed or reset between requests). Everything else is either the artisan commands (octane:start, octane:reload, octane:stop, octane:status) or the Laravel\Octane\Facades\Octane facade.

Your first real use case is almost always Octane::concurrently() — parallelising independent I/O inside a single request, which a normal single-threaded PHP-FPM request cannot do:

use Laravel\Octane\Facades\Octane;

[$users, $stats, $feed] = Octane::concurrently([
    fn () => User::active()->get(),
    fn () => DB::select('select count(*) from events'),
    fn () => Http::get('https://api.example.com/feed')->json(),
], 5000); // millisecond timeout

Second is --watch in local dev, which makes the boot-once model bearable by restarting workers on file change. The FrankenPHP driver (v2.11.0+) uses FrankenPHP's built-in watcher; Swoole and RoadRunner have their own implementations.

Implementation Patterns

Inject the request, never call request() deep in the stack. Under Octane the container is shared across requests, so a Request captured in a singleton or a static will be the first request's instance forever. Type-hint Illuminate\Http\Request on controllers/middleware and pass data down explicitly.

Bound the work per worker. Use --max-requests=500 (or config('octane.max_requests'), configurable since v2.10.0) so a worker recycles after N requests. This is the practical antidote to slow leaks from third-party packages. --workers=auto sizes the pool to CPU count; since v2.15.0 Swoole's auto worker count is correct inside containers (cgroup-aware), so avoid hand-tuning unless you have measured a reason.

Swoole/OpenSwoole-only tooling — check config('octane.server') before using:

Octane::tick('metrics-flush', fn () => Metrics::flush())->seconds(10);

Octane::interval('prune', fn () => PruneOldRows::run())->everyMinute();

Octane::table('counters', ['hits' => ['type' => 'int', 'size' => 8]]);
Octane::table('counters')->incr('hits', 1);

tick/interval run in the worker loop, outside any request, so they must not depend on the current request, session, or auth context. Use tables (shared memory) for counters and rate limiting rather than the filesystem or a cache round-trip.

Hot reload without downtime. php artisan octane:reload gracefully signals workers to finish in-flight requests and reboot (the command was wired into the package as a first-class command in v2.13.2). Use it after deploys instead of octane:stop; pair it with octane:status for health checks.

Deployment. FrankenPHP ships a Caddyfile stub and frankenphp-worker.php; the v2.14.0 release widened the set of environment variables injected into the Caddyfile, so you can configure it via .env. Practical knobs: APP_PUBLIC_PATH (fallback logic fixed in v2.13.4) for non-standard public dirs, CADDY_EXTRA_CONFIG (v2.9.0) for arbitrary Caddy directives, GITHUB_PROXY_PREFIX (v2.13.5) for air-gapped/behind-proxy binary downloads, OCTANE_SERVER, and --rr-config for RoadRunner. If Composer's vendor/ lives outside the app root, that is supported since v2.9.2.

Prefer framework primitives that Octane already resets. Queues, Cache, DB, pagination, Str, Monolog context, Carbon locale, and Vite per-request state all have dedicated flush listeners in config/octane.php. Reach for Octane::flush()/octane:reload only for your own state; if you must reset something custom, register a listener on the request lifecycle rather than stuffing it into middleware.

Gotchas and Tips

  • The number one bug is stale state. Any static property, service-provider singleton, or class-level cache that stores request-derived data leaks between users. Grep your app for static $ and protected $something on singletons before shipping. Octane's Application::flush() / forgetScopedInstances() fixes container bindings, not your own statics — mark request-scoped services scoped() instead of singleton().
  • dd()/dump() and exit can kill a worker, not just the request. Use logging, or the octane:start terminal output (memory usage is displayed since v1.0.3) to spot runaway workers.
  • Uploads leave temp files behind. A listener cleans them (v1.2.2, fixed again in v2.12.1), but if you move files manually via Symfony's UploadedFile, use move() — the workaround for move_uploaded_file is already patched.
  • OPcache is not automatically your friend. v2.12.0 added configurable clear_opcache and static-file-serving options; clearing opcache too aggressively destroys the restart-time benefit, clearing it never means edited files go stale.
  • --watch is a dev tool. It polls the filesystem and, on network mounts, has historically needed fixes (v1.2.4). Don't run it in production.
  • Octane::concurrently() needs at least one task — it short-circuits when the array is empty (v2.13.1). Exceptions inside tasks surface at the top level; a root-level throw used to take the whole server down, fixed since v1.0.19, but never let a task throw uncaught.
  • Extension points live in config/octane.php: warm (add heavy services here to pay boot cost once), flush, listeners (subscribe to RequestReceived, RequestTerminated, TaskReceived, WorkerStarting, WorkerStopping). That's the supported way to hook the lifecycle — middleware is not, since it runs per request and not per worker event.
  • Debugging a leak: run --max-requests=1 to prove whether the issue is cross-request state; if the bug disappears, it's a leak, not a logic error. Watch the per-request memory figure printed by octane:start.
  • Version drift: Octane's changelog is dense with driver-specific quirks (Swoole vs. OpenSwoole vs. FrankenPHP). Pin the version and read the changelog before upgrading on a FrankenPHP setup — several releases are exclusively FrankenPHP/Caddyfile behaviour changes.
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.
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections
webx-ui/module-catalog-feeds
makidizajnerica/laravel-dto
matviib/scheduler
webx-ui/templates
anodyne/blade-tabler-icons
bref/laravel-websockets
anodyne/blade-hugeicons
1994/flatpack-statamic