PHP at Brewless. Served by FrankenPHP.

Every release runs on the PHP version you choose, served by FrankenPHP, in a container in your own Scaleway project. This page says what that means, including what it does not do.

PHP versions

PHP 8.3 PHP 8.4 default PHP 8.5

Name the version in brewless.yml. Without it, a release is built for 8.4. A version Brewless does not build is refused before anything is built, with the versions it does.

php: "8.4"

Built and run on one version

The build resolves your Composer dependencies on the version the release will run on. The same commit built for another version is another image.

Patched before it ships

The base image refuses to build on a PHP release that lacks the latest security fixes we know of, so an old patch version never reaches a container.

The usual extensions

pdo_pgsql, pdo_mysql, redis, intl, bcmath, pcntl, zip, gd, exif and OPcache, plus pg_dump and pdftotext for applications that shell out to them.

OPcache that never checks the disk

The code in an image never changes, so OPcache does not look for changed files. For a Laravel application, configuration, routes and views are cached when the container starts.

Not offered: PHP 8.2 and older

PHP 8.2 gets security fixes only until the end of 2026, and older versions get none. A new platform should not start on them. Upgrade to 8.3 or later before you move.

What FrankenPHP is

FrankenPHP is an application server for PHP, written in Go on top of the Caddy web server. PHP runs inside the server process itself: one program answers HTTP and runs your code, where the classic setup needs Nginx in front of a pool of PHP-FPM processes. It was started by Kévin Dunglas and is developed in the PHP project's own GitHub organisation.

Why Brewless uses it

One process per container

A serverless container runs one thing. Nginx with PHP-FPM is two processes, two configurations and a supervisor to keep both alive. FrankenPHP is one binary that starts in one step.

Your code needs no change

In its classic mode every request starts fresh, as with PHP-FPM. An application that runs on PHP-FPM today runs here without code that has to survive between requests.

One configuration file

The server is configured in a single Caddyfile. Ours compresses responses (zstd, Brotli, gzip) and caches built assets for a year. A docker/Caddyfile in your repository replaces it; the documentation says what yours has to keep.

Official images

The base image starts from the FrankenPHP project's own Docker image. It is open source, so you can build it yourself and keep running without us.

What FrankenPHP can do, and what Brewless uses

FrankenPHP can do more than Brewless switches on. This is the list, with the reason for each.

Feature At Brewless Why
Classic mode Used Each request starts with a clean state, as with PHP-FPM. Every PHP application works as it is.
Compression Used zstd, Brotli and gzip, chosen per browser, by the server itself.
Worker mode Never Keeps the application booted between requests, and with it whatever a request leaves behind: in a multi-tenant application, one tenant's data for the next. On serverless containers it saves nothing, since a container that was scaled to zero starts cold anyway. A build that asks for it is refused.
Laravel Octane Never Octane runs Laravel in worker mode, with the same risk. A start script that starts Octane is refused.
Automatic HTTPS At the edge Off in the container on purpose. Visitors connect to bunny.net, or to Scaleway without bunny.net, and that is where the certificate lives. The container only ever talks to them, over their own connection.
HTTP/3 At the edge Which HTTP version a visitor gets is decided where the connection ends: at bunny.net, which serves HTTP/2 today. Scaleway passes plain HTTP to the container, so HTTP/3 in FrankenPHP would never be reached.
103 Early Hints Not used yet FrankenPHP can send them, but Scaleway drops them before they leave its platform: we tested this in October 2026, over HTTP/1.1 and HTTP/2. bunny.net never receives them, so a browser never sees them.

The trade-offs

FrankenPHP is a good fit for serverless containers. It is not free of downsides, and you should know them.

It is younger

FrankenPHP has been stable since 2023. Nginx and PHP-FPM have run PHP for well over a decade, and some edge cases are only known there.

A smaller community

For an odd problem there are fewer answers to find than for Nginx and PHP-FPM, and fewer people who have hit it before.

Thread-safe PHP only

FrankenPHP runs PHP in threads, so it needs the thread-safe build. An extension that is not thread-safe cannot be loaded. The common ones are fine; the list above is what the image has.

A crash takes the process

When a C extension crashes, the whole server process stops, where PHP-FPM loses one child. Scaleway starts the container again, but requests in flight on it fail.

Compared to the alternatives

The common ways to serve PHP, and where each stands for a serverless container.

Server What it is For Brewless
FrankenPHP PHP inside a Go server built on Caddy. Classic mode like PHP-FPM, or a worker mode. What Brewless runs, in classic mode only.
Nginx + PHP-FPM The long-standing default: a web server in front of a pool of PHP processes. Proven, but two processes and two configurations in one container.
Apache + mod_php PHP loaded into Apache itself, one request per process. Simple, but heavier per request, and rarely chosen for containers today.
Swoole / OpenSwoole A PHP extension with its own event loop and coroutines. Fast, but the application and its libraries have to be written for a long-running process.
RoadRunner A Go server that hands requests to long-running PHP workers. Fast, with the same caveats as any worker setup, and a separate worker script.
Laravel Octane A Laravel package that runs the application in worker mode on FrankenPHP, Swoole or RoadRunner. Refused: it runs in worker mode.

Questions about PHP at Brewless

Which PHP versions does Brewless run?
Name the version with php in brewless.yml. Without it, a release is built for PHP 8.4. A version Brewless does not build is refused before anything is built, and the message names the ones it does. PHP 8.2 and older are not offered: 8.2 gets security fixes only until the end of 2026, and older versions get none.
Does Brewless support FrankenPHP worker mode or Laravel Octane?
No, both are refused. Worker mode keeps the application booted between requests, and with it whatever a request leaves behind: in a multi-tenant application, one tenant's data for the next. On serverless containers it also saves nothing, because a container that was scaled to zero starts cold anyway. A build that declares a worker in docker/Caddyfile, or a start script that starts Octane, does not get through.
What is FrankenPHP?
An application server for PHP, written in Go on top of the Caddy web server. PHP runs inside the server process itself, so one program answers HTTP and runs your code, where the classic setup needs Nginx in front of a pool of PHP-FPM processes. It was started by Kévin Dunglas and is developed in the PHP project's own GitHub organisation.
Do I have to change my Laravel code to run on FrankenPHP?
No. Brewless runs FrankenPHP in classic mode, where every request starts with a clean state, the way PHP-FPM gives you one. An application that runs on Nginx with PHP-FPM today runs here without any code that has to survive between requests.
Which PHP extensions does the Brewless image have?
pdo_pgsql, pdo_mysql, redis, intl, bcmath, pcntl, zip, gd, exif and OPcache, plus pg_dump and pdftotext for applications that shell out to them. FrankenPHP runs PHP in threads, so it needs the thread-safe build: an extension that is not thread-safe cannot be loaded. The common ones are.
Can I change the Caddy configuration or php.ini?
Yes. Both are files in your own repository: a docker/Caddyfile replaces the one Brewless ships, and a docker/php.ini adds your settings. The documentation says what your Caddyfile has to keep for a release to work, and a Caddyfile that declares a worker is refused.
How does FrankenPHP compare to PHP-FPM, Swoole and RoadRunner?
Nginx with PHP-FPM is proven but means two processes and two configurations in one container. Swoole and RoadRunner are fast, but the application and its libraries have to be written for a long-running process. FrankenPHP in classic mode keeps the request-per-request model and runs as one binary. The honest downsides: it is younger, its community is smaller, and when a C extension crashes the whole server process stops, where PHP-FPM would lose one child.

Read more

The PHP logo is by Colin Viebrock (CC BY-SA 4.0). The FrankenPHP logo is the FrankenPHP project's own. Both are shown as their makers published them.