# [RFC] Single binary for CLI and FPM

- Thread: https://externals.io/message/132806
- Original archive: https://news-web.php.net/php.internals/132806
- Started by Matthieu Napoli on 2026-10-06T10:32:16+00:00
- Messages: 12
- RFC: Single binary for CLI and FPM (Under discussion) — https://externals.io/rfc/single-binary-cli-fpm.md
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · RFCs at https://externals.io/rfc.md · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #132806 — Matthieu Napoli — 2026-10-06T10:32:16+00:00

URL: https://externals.io/message/132806#132806

Hi internals,

I'd like to start the discussion on a new RFC: https://wiki.php.net/rfc/single-binary-cli-fpm

It proposes a new `--enable-cli-fpm` build option that links the FPM SAPI into the `php` executable. A combined `php` binary runs FPM when its first argument is `--fpm`, or when it is invoked under a name starting with `php-fpm` (e.g. a `php-fpm` symlink to `php`). Everything else is the CLI, unchanged. The standalone `php-fpm` executable is still built.

```
php script.php                               # CLI, as today
php --fpm --nodaemonize -y /etc/php-fpm.conf # FPM
 
ln -s php php-fpm
./php-fpm --nodaemonize -y /etc/php-fpm.conf # FPM, through a symlink
```

The motivation is that the two executables share almost all their code. I did some measurements with [Bref](https://bref.sh) on AWS Lambda to illustrate its usefulness for this specific system. Shipping one binary instead of two:

- removes one of the two ~26 MB executables from the runtime (leaving more room under the 250 MB limit for application code)
- cuts the cold start of a minimal FPM application by 70 ms (-25%), because FPM no longer loads a second copy of the engine right after the CLI loaded it

It makes a difference on AWS Lambda, it might also help on other systems where boot speed (autoscaling, serverless, etc.) or disk size is important.

And from a simplicity perspective I like the idea of having a single binary with FPM "built-in".

There's a secondary vote on enabling the option by default, as Marc suggested when I requested RFC karma.

I'd especially welcome feedback from packagers:

1. Would you consider shipping `php-fpm` as a symlink to a combined `php` binary, with the FPM package keeping only the configuration and service files?
2. Should this stay opt-in, or be enabled by default?

The implementation is at https://github.com/php/php-src/pull/23558 (thanks Jakub and Marc for the reviews so far).

Thanks,
Matthieu Napoli

---

## #132807 — tim@bastelstu.be — 2026-10-06T12:06:48+00:00

URL: https://externals.io/message/132806#132807 · In reply to: #132806

Hi


> I'd like to start the discussion on a new RFC: 
> https://wiki.php.net/rfc/single-binary-cli-fpm

Thank you for the RFC. The proposal makes sense to me and from what I 
understand there are basically no drawbacks to combining the two 
binaries into one. I only have one concern regarding:

> `PHP_BINARY` in FPM mode is the path of the combined php executable, 
> where it is the path of php-fpm today (a symlink is resolved to its 
> target). Unlike php-fpm, that executable can also run CLI scripts.

This means scripts running in a FPM web context and relying on 
`PHP_BINARY` cannot spawn fresh FPM instances (for whatever reason), 
without adding a `--fpm` flag themselves. However I don't think it is 
possible to determine from PHP itself if the binary is a combined binary 
or not. Should we have some `PHP_FPM_IN_CLI` constant or similar that 
allows scripts to make that distinction.

The RFC also notes that the standalone PHP FPM binary will remain an 
option. Is there any reason to keep support for that, except for 
backward compatibility? I think we should strongly encourage folks to 
ship PHP as a “single binary” CLI+FPM distribution. Or just CLI where 
FPM is not required and the extra dependencies should go away. But just 
FPM doesn't feel like a reasonable use case.

Best regards
Tim Düsterhus

---

## #132808 — Matthieu Napoli — 2026-10-06T12:21:41+00:00

URL: https://externals.io/message/132806#132808 · In reply to: #132807

> This means scripts running in a FPM web context and relying on 
`PHP_BINARY` cannot spawn fresh FPM instances (for whatever reason), 
without adding a `--fpm` flag themselves.

Right, though starting a new FPM master from a script running _inside_ FPM already seems rare to me, and such code would need to know the FPM setup (config file, etc.).

The opposite case is more common though (scripts in FPM that use `PHP_BINARY` to run CLI commands. The RFC would make that simpler.

If there is a real need, I see two simpler options:

- `php-fpm` could ignore `--fpm` so that `PHP_BINARY --fpm ...` always work
- if embedding FPM is the default, then `PHP_BINARY --fpm ...` should work in most cases then

> The RFC also notes that the standalone PHP FPM binary will remain an option. Is there any reason to keep support for that, except for backward compatibility?

No. I'd find this preferable to drop `php-fpm` in a separate step/RFC since that's a fairly big breaking change for packagers.

Matthieu

---

## #132809 — David Maye — 2026-10-06T12:25:42+00:00

URL: https://externals.io/message/132806#132809 · In reply to: #132806

I like this idea. Aesthetically, it makes PHP look more like mainstream languages such as Ruby or Python, in my opinion.

Something interesting is that on Linux when configuring a PHP web app with Nginx, every request goes from the server to the php-fpm service (i.e: `php8.4-fpm.socket` on Debian). I'm actually on openSUSE Leap 16.0, and the socket/service execution step looks like the following:

```
ExecStart=/usr/sbin/php-fpm --nodaemonize --fpm-config /etc/php8/fpm/php-fpm.conf
```

So yeah, if the PHP-FPM looks like PHP-CLI, then migrating to a single binary would be ok, as the dynamic libphp.so library is not an option based on your RFC (co.

However, I have a concern about the OS package maintainers from major Linux package repositories (dpkg, rpm and others). I think, in the case that this becomes the default behavior, they would need to be aware that PHP-FPM is now on a symlink of PHP-CLI (else what happens when you download PHP-FPM?). Because they would need to take the required steps to rebundle PHP (configure the symlink of PHP-FPM to php-cli or deprecate it). Something similar to mysql now being a symlink of now MariaDB by default would take in place. 

Not forgetting that all of those operating systems would have to configure the PHP-CLI package to include the necessary service manager (systemd) script previously available on PHP-FPM. Because there are also a lot of IT infrastructures that depend on it (think about Docker containers, for instance). Also, how would it clash with users of OSes without systemd (openrc, runit)?. 

For the rest, the idea is cool. My concern is around the maintainers of these packages. I think the safest action would be to have it first as a new optional `./configure` flag and then progressively pass it to a default behavior. Because this would maybe enable use cases like serverless PHP (the https://bref.sh project) to be able to have that option by default from a specific Docker image of PHP with that config. 

And well, I think that having a shared library `libphp.so` would actually be a way easier solution to fix it. I have a question about that approach: Isn't it possible to do it by default without any PHP modification?

Kind Regards,
David Maye.

> El 06/10/2026 12:32 Matthieu Napoli <matthieu@mnapoli.fr> escribió:
> 
>  
> Hi internals,
> 
> I'd like to start the discussion on a new RFC: https://wiki.php.net/rfc/single-binary-cli-fpm
> 
> It proposes a new `--enable-cli-fpm` build option that links the FPM SAPI into the `php` executable. A combined `php` binary runs FPM when its first argument is `--fpm`, or when it is invoked under a name starting with `php-fpm` (e.g. a `php-fpm` symlink to `php`). Everything else is the CLI, unchanged. The standalone `php-fpm` executable is still built.
> 
> ```
> php script.php                               # CLI, as today
> php --fpm --nodaemonize -y /etc/php-fpm.conf # FPM
>  
> ln -s php php-fpm
> ./php-fpm --nodaemonize -y /etc/php-fpm.conf # FPM, through a symlink
> ```
> 
> The motivation is that the two executables share almost all their code. I did some measurements with [Bref](https://bref.sh) on AWS Lambda to illustrate its usefulness for this specific system. Shipping one binary instead of two:
> 
> - removes one of the two ~26 MB executables from the runtime (leaving more room under the 250 MB limit for application code)
> - cuts the cold start of a minimal FPM application by 70 ms (-25%), because FPM no longer loads a second copy of the engine right after the CLI loaded it
> 
> It makes a difference on AWS Lambda, it might also help on other systems where boot speed (autoscaling, serverless, etc.) or disk size is important.
> 
> And from a simplicity perspective I like the idea of having a single binary with FPM "built-in".
> 
> There's a secondary vote on enabling the option by default, as Marc suggested when I requested RFC karma.
> 
> I'd especially welcome feedback from packagers:
> 
> 1. Would you consider shipping `php-fpm` as a symlink to a combined `php` binary, with the FPM package keeping only the configuration and service files?
> 2. Should this stay opt-in, or be enabled by default?
> 
> The implementation is at https://github.com/php/php-src/pull/23558 (thanks Jakub and Marc for the reviews so far).
> 
> Thanks,
> Matthieu Napoli

---

## #132811 — Ayesh Karunaratne — 2026-10-06T14:23:25+00:00

URL: https://externals.io/message/132806#132811 · In reply to: #132806

Hi Matthieu,

> The motivation is that the two executables share almost all their code. I did some measurements with [Bref](https://bref.sh) on AWS Lambda to illustrate its usefulness for this specific system. Shipping one binary instead of two:
>
> - removes one of the two ~26 MB executables from the runtime (leaving more room under the 250 MB limit for application code)
> - cuts the cold start of a minimal FPM application by 70 ms (-25%), because FPM no longer loads a second copy of the engine right after the CLI loaded it
>
> It makes a difference on AWS Lambda, it might also help on other systems where boot speed (autoscaling, serverless, etc.) or disk size is important.
>
> And from a simplicity perspective I like the idea of having a single binary with FPM "built-in".
>
> There's a secondary vote on enabling the option by default, as Marc suggested when I requested RFC karma.
>
> I'd especially welcome feedback from packagers:
>
> 1. Would you consider shipping `php-fpm` as a symlink to a combined `php` binary, with the FPM package keeping only the configuration and service files?
> 2. Should this stay opt-in, or be enabled by default?
>

I like the direction of the proposal and believe it will be a positive change.

In Debian/Ubuntu, shipping SAPIs as separate packages (`php-cli`,
`php-fpm`, `libapache2-mod-php8.6`, etc) is the norm, and to not break
the packages, I would imagine symlinking would be necessary one way or
the other. Alternately, php-fpm could be a shim that calls CLI with
`--fpm` flag, and it depends on `php-cli` package.

I'm also concerned about CLI and FPM parameters. For example, CLI has
`-F` argument is to execute a file, but FPM's `-F` flag is an alias to
`--nodaemonize`. Changing what they do based on the binary/symlink
name or another flag (such as `--fpm`) is not really a good idea.

Thank you,
Ayesh.

---

## #132812 — tim@bastelstu.be — 2026-10-06T14:31:32+00:00

URL: https://externals.io/message/132806#132812 · In reply to: #132808

Hi


> If there is a real need, I see two simpler options:

I'm not sure there is a *real need*, but it's a theoretical breaking 
change with no way to work around from what I can tell. A hypothetical 
use case could be “spawn customer-specific FPM pools from a PHP-based 
control panel”, for example for some playground.

> - `php-fpm` could ignore `--fpm` so that `PHP_BINARY --fpm ...` always 
> work

That suggestion makes sense to me and would resolve the above: `php-fpm` 
would just mean `php --fpm` and `php --fpm --fpm` would then just be a 
duplicate `--fpm` flag. Ordering matters with regard to `--fpm`, but 
that seems obvious enough to explain.

> > […]
> 
> No. I'd find this preferable to drop `php-fpm` in a separate step/RFC 
> since that's a fairly big breaking change for packagers.

Should the “this” in “this preferable” mean “it” instead? The “this” in 
your phrasing could refer to my suggestion, which doesn't make sense to 
me.

In any case, packagers already need to deal with all kinds of packaging 
changes (e.g. OPcache becoming non-optional in PHP 8.5). I'm not sure 
adding one additional symlink and/or replacing `php-fpm` by `php --fpm` 
in the service definition is any more complicated than that. Doing it 
all in a single step would also mean that there is no weird intermediate 
state where when providing support we would also need to differentiate 
between “FPM” and “FPM as part of CLI”.

Best regard
Tim Düsterhus

---

## #132816 — Larry Garfield — 2026-10-06T16:27:02+00:00

URL: https://externals.io/message/132806#132816 · In reply to: #132806

> Hi internals,
>
> I'd like to start the discussion on a new RFC: 
> https://wiki.php.net/rfc/single-binary-cli-fpm
>
> It proposes a new `--enable-cli-fpm` build option that links the FPM 
> SAPI into the `php` executable. A combined `php` binary runs FPM when 
> its first argument is `--fpm`, or when it is invoked under a name 
> starting with `php-fpm` (e.g. a `php-fpm` symlink to `php`). Everything 
> else is the CLI, unchanged. The standalone `php-fpm` executable is 
> still built.
>
> ```
> php script.php                               # CLI, as today
> php --fpm --nodaemonize -y /etc/php-fpm.conf # FPM
> 
> ln -s php php-fpm
> ./php-fpm --nodaemonize -y /etc/php-fpm.conf # FPM, through a symlink
> ```
>
> The motivation is that the two executables share almost all their code. 
> I did some measurements with [Bref](https://bref.sh) on AWS Lambda to 
> illustrate its usefulness for this specific system. Shipping one binary 
> instead of two:
>
> - removes one of the two ~26 MB executables from the runtime (leaving 
> more room under the 250 MB limit for application code)
> - cuts the cold start of a minimal FPM application by 70 ms (-25%), 
> because FPM no longer loads a second copy of the engine right after the 
> CLI loaded it
>
> It makes a difference on AWS Lambda, it might also help on other 
> systems where boot speed (autoscaling, serverless, etc.) or disk size 
> is important.
>
> And from a simplicity perspective I like the idea of having a single 
> binary with FPM "built-in".
>
> There's a secondary vote on enabling the option by default, as Marc 
> suggested when I requested RFC karma.
>
> I'd especially welcome feedback from packagers:
>
> 1. Would you consider shipping `php-fpm` as a symlink to a combined 
> `php` binary, with the FPM package keeping only the configuration and 
> service files?
> 2. Should this stay opt-in, or be enabled by default?
>
> The implementation is at https://github.com/php/php-src/pull/23558 
> (thanks Jakub and Marc for the reviews so far).
>
> Thanks,
> Matthieu Napoli

I can see the value here, and I am not opposed in concept.  My one question is that this essentially privileges the FPM SAPI, right at the time that we're seeing more and more alternate SAPIs.  (FrankenPHP, Swoole, various async tools that just run on the CLI, etc.). Would there be any negative impact for them?  If at some point in the future another SAPI (eg, FrankenPHP or something similar to it) became a first-class web-serving SAPI in php-src, would we then need to package multiple variants?  (FPM-CLI, Franken-CLI, etc.). 

I just want to make sure we don't inadvertently lock in FPM as The Way PHP Runs(tm) just at the time that it no longer is.

--Larry Garfield

---

## #132819 — Nicolas Grekas — 2026-10-06T17:55:57+00:00

URL: https://externals.io/message/132806#132819 · In reply to: #132816

Le mar. 6 oct. 2026, 18:30, Larry Garfield <larry@garfieldtech.com> a
écrit :

> 
> > […]
>
> I can see the value here, and I am not opposed in concept.  My one
> question is that this essentially privileges the FPM SAPI, right at the
> time that we're seeing more and more alternate SAPIs.  (FrankenPHP, Swoole,
> various async tools that just run on the CLI, etc.). Would there be any
> negative impact for them?  If at some point in the future another SAPI (eg,
> FrankenPHP or something similar to it) became a first-class web-serving
> SAPI in php-src, would we then need to package multiple variants?
> (FPM-CLI, Franken-CLI, etc.).
>
> I just want to make sure we don't inadvertently lock in FPM as The Way PHP
> Runs(tm) just at the time that it no longer is.
>
> --Larry Garfield
>


I share this concern.
The fpm binary should embed the cli if we go this way. Not the other way
around.
Same as frankenphp shipping also the cli, especially since it's been made
easier in PHP 8.6.

Nicolas

---

## #132821 — Jakub Zelenka — 2026-10-06T19:15:22+00:00

URL: https://externals.io/message/132806#132821 · In reply to: #132819

On Tue, Oct 6, 2026 at 7:59 PM Nicolas Grekas <nicolas.grekas+php@gmail.com>
wrote:

>
>
> Le mar. 6 oct. 2026, 18:30, Larry Garfield <larry@garfieldtech.com> a
> écrit :
>
> > […]
>
>
> I share this concern.
> The fpm binary should embed the cli if we go this way. Not the other way
> around.
> Same as frankenphp shipping also the cli, especially since it's been made
> easier in PHP 8.6.
>
>
I was actually thinking about this and it really makes more sense and also
better in terms of implementation as we will just do what FrankenPHP does.

Kind regards,

Jakub

---

## #132834 — Matthieu Napoli — 2026-10-08T13:53:13+00:00

URL: https://externals.io/message/132806#132834 · In reply to: #132821

> > […]
>
>
> I was actually thinking about this and it really makes more sense and also
> better in terms of implementation as we will just do what FrankenPHP does.

Right, good point, I agree too, thanks!

I opened a new PR that goes the other way around: https://github.com/php/php-src/pull/24192

I have also updated the RFC: https://wiki.php.net/rfc/single-binary-cli-fpm

- `php` CLI is unchanged
- `php-fpm` *always* includes `php` (and runs as CLI when run as `php` instead of `php-fpm`)
- no need for a build option anymore
- the diff is simpler
- FPM aligns with FrankenPHP (both include the CLI)
- No `--fpm` CLI option anymore, solves the objections raised previously in this thread

That sounds much better indeed.

---

## #132835 — tim@bastelstu.be — 2026-10-08T14:25:14+00:00

URL: https://externals.io/message/132806#132835 · In reply to: #132834

Hi


> Right, good point, I agree too, thanks!
> 
> I opened a new PR that goes the other way around: 
> https://github.com/php/php-src/pull/24192
> 
> I have also updated the RFC: 
> https://wiki.php.net/rfc/single-binary-cli-fpm
> 
> - `php` CLI is unchanged
> - `php-fpm` *always* includes `php` (and runs as CLI when run as `php` 
> instead of `php-fpm`)
> - no need for a build option anymore
> - the diff is simpler
> - FPM aligns with FrankenPHP (both include the CLI)
> - No `--fpm` CLI option anymore, solves the objections raised 
> previously in this thread
> 
> That sounds much better indeed.

I disagree that this is better from a discoverability and predictability 
point of view and I also disagree with the concerns that the original 
proposal would somehow “privilege” FPM.

As for the first part:

When running a PHP binary on the command line, my expectation is that 
I'm running a CLI application and thus I expect to end up in the CLI 
SAPI. Suddenly starting FPM, which potentially binds ports or sockets, 
just because I happen to name the binary the wrong name (e.g. when using 
a custom `p` symlink to run PHP after my distro switched to the 
“combined binary” setup or just `php-8.7` that the RFC notes as running 
FPM) would be very surprising. With the current proposal I also have no 
way out, except naming the binary differently. Changing behavior based 
on the binary name to do entirely different things is something that is 
comparatively rarely used and thus folks are unlikely to be accustomed 
to this being a thing (the most prominent example that comes to mind 
would be Busybox). An explicit flag / argument would be properly 
discoverable (appearing in help outputs) and would also be reliably 
scriptable.

For the second point, and tying into the first: PHP FPM is a / the 
recommended solution of running PHP as a FastCGI application. Adding an 
`--fpm` flag to the PHP binary to run PHP in “FastCGI mode” would match 
the `-S` flag to run PHP in “HTTP mode” (which switches from the `cli` 
to the `cli-server` SAPI). I understand that the current “HTTP mode” 
implementation is meant to be used for development purposes only, but 
that is an implementation detail. We could in theory implement a 
production grade web server to PHP tomorrow (and potentially add 
`--http` as the entrypoint for the “HTTP mode”, keeping `-S` as an 
alias) and then we would have:

     php # CLI mode
     php --fpm # FastCGI mode
     php --http # HTTP mode

with the `php` binary being the singular entrypoint for all use cases 
where PHP is running “standalone” rather than embedded into something 
else (as with mod_php). Alternatively, we use `--mode=fpm` to switch to 
FPM mode instead of reserving the top level `--fpm` flag.

Best regards
Tim Düsterhus

---

## #132861 — Levi Morrison — 2026-10-10T19:07:12+00:00

URL: https://externals.io/message/132806#132861 · In reply to: #132806

>
> Hi internals,
>
> I'd like to start the discussion on a new RFC: https://wiki.php.net/rfc/single-binary-cli-fpm
>
> It proposes a new `--enable-cli-fpm` build option that links the FPM SAPI into the `php` executable. A combined `php` binary runs FPM when its first argument is `--fpm`, or when it is invoked under a name starting with `php-fpm` (e.g. a `php-fpm` symlink to `php`). Everything else is the CLI, unchanged. The standalone `php-fpm` executable is still built.
>
> ```
> php script.php                               # CLI, as today
> php --fpm --nodaemonize -y /etc/php-fpm.conf # FPM
>
> ln -s php php-fpm
> ./php-fpm --nodaemonize -y /etc/php-fpm.conf # FPM, through a symlink
> ```
>
> The motivation is that the two executables share almost all their code. I did some measurements with [Bref](https://bref.sh) on AWS Lambda to illustrate its usefulness for this specific system. Shipping one binary instead of two:
>
> - removes one of the two ~26 MB executables from the runtime (leaving more room under the 250 MB limit for application code)
> - cuts the cold start of a minimal FPM application by 70 ms (-25%), because FPM no longer loads a second copy of the engine right after the CLI loaded it
>
> It makes a difference on AWS Lambda, it might also help on other systems where boot speed (autoscaling, serverless, etc.) or disk size is important.
>
> And from a simplicity perspective I like the idea of having a single binary with FPM "built-in".
>
> There's a secondary vote on enabling the option by default, as Marc suggested when I requested RFC karma.
>
> I'd especially welcome feedback from packagers:
>
> 1. Would you consider shipping `php-fpm` as a symlink to a combined `php` binary, with the FPM package keeping only the configuration and service files?
> 2. Should this stay opt-in, or be enabled by default?
>
> The implementation is at https://github.com/php/php-src/pull/23558 (thanks Jakub and Marc for the reviews so far).
>
> Thanks,
> Matthieu Napoli

Perhaps there's another alternative: libzend.so. It was actually
possible to build this in the past. I assume it was partly removed in
the never-ending quest for more performance, but it is something I
have considered for other reasons as well. One reason is that it
allows (most) extensions to link their symbols to libzend.so instead
of leaving them undefined for the binary to provide. In any case, the
performance penalty would be something to measure, but I would prefer
this solution if it's viable, personally.
