By default, FrankenPHP tries to offer a good compromise between performance and ease of use. However, it is possible to substantially improve performance using an appropriate configuration.
By default, FrankenPHP starts 2 times more threads and workers (in worker mode) than the available numbers of CPU.
The appropriate values depend heavily on how your application is written, what it does and your hardware. We strongly recommend changing these values.
To find the right values, it's best to run load tests simulating real traffic. k6 and Gatling are good tools for this.
To configure the number of threads, use the num_threads
option of the php_server
and php
directives.
To change the number of workers, use the num
option of the worker
section of the frankenphp
directive.
Enabling the worker mode dramatically improves performance, but your app must be adapted to be compatible with this mode: you need to create a worker script and to be sure that the app is not leaking memory.
The static binaries we provide and the Alpine Linux variant of the official Docker images are using the musl libc.
PHP is known to be significantly slower when using this alternative C library instead of the traditional GNU library, especially when compiled in ZTS mode (thread-safe), which is required for FrankenPHP.
Also, some bugs also only happen when using musl.
In production environements, we strongly recommend to use the glibc.
This can be achieved by using the Debian Docker images (the default) and by compiling FrankenPHP from sources.
Alternatively, we provide static binaries compiled with the mimalloc allocator, which makes FrankenPHP+musl faster (but still slower than FrankenPHP+glibc).
FrankenPHP is written in Go.
In general, the Go runtime doesn't require any special configuration, but in certain circumstances, specific configuration improves performance.
You likely want to set the GODEBUG
environment variable to cgocheck=0
(the default in the FrankenPHP Docker images).
If you run FrankenPHP in containers (Docker, Kubernetes, LXC...) and limit the memory available for the containers,
set the GOMEMLIMIT
environment variable to the available amount of memory.
For more details, the Go documentation page dedicated to this subject is a must-read to get the most out of the runtime.
[!TIP]
The default
Caddyfile
provided in the Docker images already contains this optimization.
When not explicitly set, FrankenPHP sets the root
option of the php_server
directive to the {http.vars.root}
placeholder.
The value of this placeholder can be configured using the root
directive provided by Caddy
and can contain dynamic values.
While convenient, this feature comes at a cost in terms of performance because the root directory is resolved at runtime, and, depending on your configuration, may change with each request.
Explicitly set the root directory in the php_server
directive to avoid this cost:
php_server {
root /path/to/your/public/dir/
}
Alternatively, you can use the PWD
environment variable,
which always contain the path to the current directory on POSIX systems (including on Linux, macOS and FreeBSD).
This variable will only be resolved once, at Caddy startup:
php_server {
root {$PWD}/public/
}
file_server
By default, the php_server
directive automatically sets up a file server to
serve static files (assets) stored in the root directory.
This feature is convenient, but comes with a cost. To disable it, use the following config:
php_server {
file_server off
}
You can use placeholders in the root
and env
directives.
However, this prevents caching these values, and comes with a significant performance cost.
If possible, avoid placeholders in these directives.
resolve_root_symlink
By default, if the document root is a symbolic link, it is automatically resolved by FrankenPHP (this is necessary for PHP to work properly). If the document root is not a symlink, you can disable this feature.
php_server {
resolve_root_symlink false
}
This will improve performance if the root
directive contains placeholders.
The gain will be negligible in other cases.
Logging is obviously very useful, but, by definition, it requires I/O operations and memory allocations, which considerably reduces performance. Make sure you set the logging level correctly, and only log what's necessary.
FrankenPHP uses the official PHP interpreter. All usual PHP-related performance optimizations apply with FrankenPHP.
In particular:
realpath
cache is big enough for the needs of your applicationFor more details, read the dedicated Symfony documentation entry (most tips are useful even if you don't use Symfony).