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 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:
- Would you consider shipping
php-fpmas a symlink to a combinedphpbinary, with the FPM package keeping only the configuration and service files? - 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
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_BINARYin 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
This means scripts running in a FPM web context and relying on
PHP_BINARYcannot spawn fresh FPM instances (for whatever reason),
without adding a--fpmflag 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-fpmcould ignore--fpmso thatPHP_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
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-fpmcould ignore--fpmso thatPHP_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.
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-fpmin 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
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-fpmbuild option that links the FPM SAPI into thephpexecutable. A combinedphpbinary runs FPM when its first argument is--fpm, or when it is invoked under a name starting withphp-fpm(e.g. aphp-fpmsymlink tophp). Everything else is the CLI, unchanged. The standalonephp-fpmexecutable 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 symlinkThe motivation is that the two executables share almost all their code. I did some measurements with Bref 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:
- Would you consider shipping
php-fpmas a symlink to a combinedphpbinary, with the FPM package keeping only the configuration and service files?- 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
Hi Matthieu,
The motivation is that the two executables share almost all their code. I did some measurements with Bref 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:
- Would you consider shipping
php-fpmas a symlink to a combinedphpbinary, with the FPM package keeping only the configuration and service files?- 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.
Hi internals,
I'd like to start the discussion on a new RFC:
https://wiki.php.net/rfc/single-binary-cli-fpmIt proposes a new
--enable-cli-fpmbuild option that links the FPM
SAPI into thephpexecutable. A combinedphpbinary runs FPM when
its first argument is--fpm, or when it is invoked under a name
starting withphp-fpm(e.g. aphp-fpmsymlink tophp). Everything
else is the CLI, unchanged. The standalonephp-fpmexecutable 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 symlinkThe motivation is that the two executables share almost all their code.
I did some measurements with Bref 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 itIt 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:
- Would you consider shipping
php-fpmas a symlink to a combined
phpbinary, with the FPM package keeping only the configuration and
service files?- 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
Le mar. 6 oct. 2026, 18:30, Larry Garfield larry@garfieldtech.com a
écrit :
Hi internals,
I'd like to start the discussion on a new RFC:
https://wiki.php.net/rfc/single-binary-cli-fpmIt proposes a new
--enable-cli-fpmbuild option that links the FPM
SAPI into thephpexecutable. A combinedphpbinary runs FPM when
its first argument is--fpm, or when it is invoked under a name
starting withphp-fpm(e.g. aphp-fpmsymlink tophp). Everything
else is the CLI, unchanged. The standalonephp-fpmexecutable 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 symlinkThe motivation is that the two executables share almost all their code.
I did some measurements with Bref 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 itIt 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:
- Would you consider shipping
php-fpmas a symlink to a combined
phpbinary, with the FPM package keeping only the configuration and
service files?- 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 NapoliI 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
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 :Hi internals,
I'd like to start the discussion on a new RFC:
https://wiki.php.net/rfc/single-binary-cli-fpmIt proposes a new
--enable-cli-fpmbuild option that links the FPM
SAPI into thephpexecutable. A combinedphpbinary runs FPM when
its first argument is--fpm, or when it is invoked under a name
starting withphp-fpm(e.g. aphp-fpmsymlink tophp). Everything
else is the CLI, unchanged. The standalonephp-fpmexecutable 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 symlinkThe motivation is that the two executables share almost all their code.
I did some measurements with Bref 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 itIt 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:
- Would you consider shipping
php-fpmas a symlink to a combined
phpbinary, with the FPM package keeping only the configuration and
service files?- 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 NapoliI 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.
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
this essentially privileges the FPM SAPI, right at the
time that we're seeing more and more alternate SAPIs. (FrankenPHP, Swoole, […]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.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.
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
phpCLI is unchangedphp-fpmalways includesphp(and runs as CLI when run asphpinstead ofphp-fpm)- no need for a build option anymore
- the diff is simpler
- FPM aligns with FrankenPHP (both include the CLI)
- No
--fpmCLI option anymore, solves the objections raised previously in this thread
That sounds much better indeed.
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/24192I have also updated the RFC:
https://wiki.php.net/rfc/single-binary-cli-fpm
phpCLI is unchangedphp-fpmalways includesphp(and runs as CLI when run asphp
instead ofphp-fpm)- no need for a build option anymore
- the diff is simpler
- FPM aligns with FrankenPHP (both include the CLI)
- No
--fpmCLI option anymore, solves the objections raised
previously in this threadThat 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
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-fpmbuild option that links the FPM SAPI into thephpexecutable. A combinedphpbinary runs FPM when its first argument is--fpm, or when it is invoked under a name starting withphp-fpm(e.g. aphp-fpmsymlink tophp). Everything else is the CLI, unchanged. The standalonephp-fpmexecutable 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 symlinkThe motivation is that the two executables share almost all their code. I did some measurements with Bref 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:
- Would you consider shipping
php-fpmas a symlink to a combinedphpbinary, with the FPM package keeping only the configuration and service files?- 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.