Home: Roshan Kc

← All posts

PHP · Performance 9 min read

PHP Runtimes Explained: FPM vs Octane vs FrankenPHP vs Swoole vs RoadRunner

Direct answer: PHP-FPM boots your app fresh on every request: simple, isolated and still the default. Swoole, RoadRunner and FrankenPHP keep the app in memory between requests, which removes bootstrap cost and typically gives several times more throughput on framework-heavy endpoints. Laravel Octane isn't a runtime; it's Laravel's adapter for those three. Choose FPM for simplicity, FrankenPHP for the easiest modern setup, Swoole when you need coroutines and in-process concurrency, and RoadRunner if your team lives in Go tooling.

We run Laravel Octane on Swoole in production at MarginTop and publish our own Swoole + io_uring runtime image. This comparison is part of my PHP in 2026 series.

The two execution models#

Shared-nothing: boot, handle, forget#

Classic PHP (FPM, mod_php, CGI) starts every request with a clean slate: load the framework, register services, handle the request, throw everything away. Opcache keeps compiled code in memory, but your application's objects, container and config are rebuilt every time.

That's PHP's superpower and its cost. A memory leak or a global variable can't outlive one request, but a large framework spends milliseconds booting before it does any real work.

Long-running workers: boot once, serve many#

Swoole, RoadRunner and FrankenPHP's worker mode boot your application once per worker and then feed it request after request. Bootstrap disappears from the hot path. The price is that state now survives between requests: static properties, singletons and anything you cache on an object. That's the source of almost every Octane bug.

Side-by-side comparison#

RuntimeWhat it isModelBest forWatch out for
PHP-FPMPHP's FastCGI process manager, behind nginx or CaddyShared-nothingMost apps, simple ops, shared hosting, maximum compatibilityBootstrap cost per request, and worker count limits concurrency
Apache mod_phpPHP embedded in ApacheShared-nothingLegacy setupsTies PHP to Apache's process model. It was the highest-risk setup in PHP2Shell.
FrankenPHPA PHP app server written in Go on top of CaddyClassic or worker modeNew projects, single-binary deploys, automatic HTTPS, HTTP/2 and HTTP/3Worker mode has the same state-leak rules as any long-running runtime
SwooleA C/C++ PHP extension with its own HTTP server and coroutine schedulerLong-running plus coroutinesHigh concurrency, WebSockets, parallel I/O inside a requestSteeper learning curve. Some extensions and libraries aren't coroutine-safe.
RoadRunnerA Go application server that manages PHP worker processesLong-runningTeams wanting queues, gRPC and plugins in one Go binaryAnother moving part outside PHP to configure and monitor
Laravel OctaneLaravel's adapter that runs your app on Swoole, RoadRunner or FrankenPHPUses the driver's modelLaravel apps wanting long-running performance with framework supportYour code must be safe to reuse across requests

FrankenPHP: the newest option, now officially backed#

FrankenPHP, created by Kévin Dunglas, embeds PHP inside the Caddy web server. One binary gives you a web server with automatic HTTPS, HTTP/3, early hints and an optional worker mode. In May 2025 the PHP Foundation announced official support for FrankenPHP, and the project moved under the PHP GitHub organisation. That's a strong signal it's here to stay.

Its biggest advantage is the gentle on-ramp. You can run it in classic mode as a drop-in replacement for nginx + FPM, then switch to worker mode when you're ready.

Swoole: when you need concurrency, not just speed#

Swoole is the only option here that gives PHP coroutines: lightweight tasks that yield while waiting on I/O. That enables parallel database and HTTP calls inside one request (Octane's Octane::concurrently()), WebSocket servers and long-lived connections in the same process. That is why it fits device-connected and real-time workloads like many of the systems we build. The details are in how we use Octane + Swoole, and the async picture is in PHP async in 2026.

RoadRunner: a Go supervisor for PHP workers#

RoadRunner, from Spiral Scout, is a Go server that keeps a pool of PHP worker processes and talks to them over a fast binary protocol. It bundles plugins for HTTP, queues, gRPC, key-value storage and more. It's a good fit if you like configuring infrastructure in one YAML file and want PHP to stay plain PHP, with no extension to compile.

The rule every long-running runtime shares#

Whatever you pick, the same discipline applies once workers live longer than a request:

  • No request data in static properties or singletons. The current user, tenant or locale must be resolved per request.
  • Reset what you cache. Octane and frameworks provide hooks for this. Use them.
  • Recycle workers. Set a max-requests limit so slow memory leaks can't accumulate.
  • Reload on deploy. Workers keep old code in memory until they restart. That also applies to security patches: after PHP2Shell, upgrading the package did nothing until workers restarted.

How to choose#

If you…Choose
Want the simplest, most compatible setup, or performance is already finePHP-FPM behind nginx or Caddy
Are starting fresh and want one binary with HTTPS and HTTP/3 built inFrankenPHP (classic mode first, worker mode later)
Need WebSockets, coroutines or parallel I/O inside a requestSwoole (via Octane for Laravel)
Want queues, gRPC and HTTP in one Go-managed serverRoadRunner
Run Laravel and bootstrap time is a measured bottleneckOctane on whichever driver above fits

One warning from experience: profile first. If your slow endpoint spends 300 ms in the database, switching runtimes saves you the 10–20 ms of bootstrap and nothing else. Fix the queries, then change runtimes.

Key takeaways#

  • FPM boots per request (simple and isolated). Swoole, RoadRunner and FrankenPHP boot once and reuse workers (much faster, but state leaks are possible).
  • Laravel Octane is an adapter, not a runtime. It runs on Swoole, RoadRunner or FrankenPHP.
  • FrankenPHP has been officially supported by the PHP Foundation since May 2025 and is the easiest modern option.
  • Swoole is the choice when you need coroutines and in-process concurrency.
  • Long-running workers must be reloaded to pick up code changes and security patches.

Sources#