2022-10-13 18:05:22 +02:00
# Using FrankenPHP Workers
2022-10-14 12:20:57 +02:00
Boot your application once and keep it in memory.
FrankenPHP will handle incoming requests in a few milliseconds.
2023-12-06 18:18:16 +01:00
## Starting Worker Scripts
### Docker
Set the value of the `FRANKENPHP_CONFIG` environment variable to `worker /path/to/your/worker/script.php` :
``` console
docker run \
2024-01-29 10:06:27 -03:00
-e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \
2023-12-06 18:18:16 +01:00
-v $PWD:/app \
2024-01-20 11:49:16 +01:00
-p 80:80 -p 443:443 -p 443:443/udp \
2023-12-06 18:18:16 +01:00
dunglas/frankenphp
```
### Standalone Binary
Use the `--worker` option of the `php-server` command to serve the content of the current directory using a worker:
``` console
2024-10-15 18:06:21 +02:00
frankenphp php-server --worker /path/to/your/worker/script.php
2023-12-06 18:18:16 +01:00
```
2024-10-07 13:17:24 +02:00
If your PHP app is [embedded in the binary ](embed.md ), you can add a custom `Caddyfile` in the root directory of the app.
2024-02-27 17:21:48 +01:00
It will be used automatically.
2024-10-07 13:17:24 +02:00
It's also possible to [restart the worker on file changes ](config.md#watching-for-file-changes ) with the `--watch` option.
The following command will trigger a restart if any file ending in `.php` in the `/path/to/your/app/` directory or subdirectories is modified:
``` console
2025-06-02 15:54:01 +02:00
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
2024-10-07 13:17:24 +02:00
```
2025-12-24 17:33:15 +01:00
This feature is often used in combination with [hot reloading ](hot-reload.md ).
2023-10-30 22:16:09 +01:00
## Symfony Runtime
2025-12-14 17:06:28 +01:00
> [!TIP]
> The following section is only necessary prior to Symfony 7.4, where native support for FrankenPHP worker mode was introduced.
2023-10-30 22:16:09 +01:00
The worker mode of FrankenPHP is supported by the [Symfony Runtime Component ](https://symfony.com/doc/current/components/runtime.html ).
To start any Symfony application in a worker, install the FrankenPHP package of [PHP Runtime ](https://github.com/php-runtime/runtime ):
``` console
composer require runtime/frankenphp-symfony
```
2023-12-06 18:18:16 +01:00
Start your app server by defining the `APP_RUNTIME` environment variable to use the FrankenPHP Symfony Runtime:
2023-10-30 22:16:09 +01:00
``` console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
-v $PWD:/app \
2024-01-20 11:49:16 +01:00
-p 80:80 -p 443:443 -p 443:443/udp \
2023-10-30 22:16:09 +01:00
dunglas/frankenphp
```
## Laravel Octane
2024-01-20 11:49:16 +01:00
See [the dedicated documentation ](laravel.md#laravel-octane ).
2023-10-30 22:16:09 +01:00
2022-10-13 18:05:22 +02:00
## Custom Apps
2023-10-30 22:16:09 +01:00
The following example shows how to create your own worker script without relying on a third-party library:
2022-10-13 18:05:22 +02:00
``` php
< ? php
// public/index.php
// Boot your app
require __DIR__ . '/vendor/autoload.php' ;
$myApp = new \App\Kernel ();
$myApp -> boot ();
2024-01-08 03:17:08 -04:00
// Handler outside the loop for better performance (doing less work)
$handler = static function () use ( $myApp ) {
2025-10-06 09:01:49 +02:00
try {
// Called when a request is received,
// superglobals, php://input and the like are reset
echo $myApp -> handle ( $_GET , $_POST , $_COOKIE , $_FILES , $_SERVER );
} catch ( \Throwable $exception ) {
// `set_exception_handler` is called only when the worker script ends,
// which may not be what you expect, so catch and handle exceptions here
( new \MyCustomExceptionHandler ) -> handleException ( $exception );
}
2024-01-08 03:17:08 -04:00
};
2024-02-27 17:21:48 +01:00
2024-06-08 13:06:40 +02:00
$maxRequests = ( int )( $_SERVER [ 'MAX_REQUESTS' ] ? ? 0 );
2024-06-11 09:55:35 +02:00
for ( $nbRequests = 0 ; ! $maxRequests || $nbRequests < $maxRequests ; ++ $nbRequests ) {
2024-06-08 13:06:40 +02:00
$keepRunning = \frankenphp_handle_request ( $handler );
2022-10-13 18:05:22 +02:00
// Do something after sending the HTTP response
$myApp -> terminate ();
2022-12-20 18:04:01 +01:00
// Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation
gc_collect_cycles ();
2024-06-08 13:06:40 +02:00
if ( ! $keepRunning ) break ;
2024-01-08 03:17:08 -04:00
}
2024-02-27 17:21:48 +01:00
2022-10-13 18:05:22 +02:00
// Cleanup
$myApp -> shutdown ();
```
2023-12-01 17:26:21 +01:00
Then, start your app and use the `FRANKENPHP_CONFIG` environment variable to configure your worker:
2022-10-13 18:05:22 +02:00
2023-10-30 22:16:09 +01:00
``` console
2022-10-13 18:05:22 +02:00
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-v $PWD:/app \
2024-01-20 11:49:16 +01:00
-p 80:80 -p 443:443 -p 443:443/udp \
2022-10-13 18:05:22 +02:00
dunglas/frankenphp
```
2024-02-27 17:21:48 +01:00
By default, 2 workers per CPU are started.
2022-10-13 18:05:22 +02:00
You can also configure the number of workers to start:
2023-10-30 22:16:09 +01:00
``` console
2022-10-13 18:05:22 +02:00
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
-v $PWD:/app \
2024-01-20 11:49:16 +01:00
-p 80:80 -p 443:443 -p 443:443/udp \
2022-10-13 18:05:22 +02:00
dunglas/frankenphp
```
2023-10-30 22:16:09 +01:00
### Restart the Worker After a Certain Number of Requests
2022-10-13 18:05:22 +02:00
2023-10-30 22:16:09 +01:00
As PHP was not originally designed for long-running processes, there are still many libraries and legacy codes that leak memory.
A workaround to using this type of code in worker mode is to restart the worker script after processing a certain number of requests:
2022-10-13 18:05:22 +02:00
2023-10-31 00:09:27 +01:00
The previous worker snippet allows configuring a maximum number of request to handle by setting an environment variable named `MAX_REQUESTS` .
2024-06-05 15:24:16 +02:00
2025-02-19 20:39:33 +01:00
### Restart Workers Manually
While it's possible to restart workers [on file changes ](config.md#watching-for-file-changes ), it's also possible to restart all workers
gracefully via the [Caddy admin API ](https://caddyserver.com/docs/api ). If the admin is enabled in your
[Caddyfile ](config.md#caddyfile-config ), you can ping the restart endpoint with a simple POST request like this:
``` console
curl -X POST http://localhost:2019/frankenphp/workers/restart
```
2024-10-03 21:53:12 +02:00
### Worker Failures
If a worker script crashes with a non-zero exit code, FrankenPHP will restart it with an exponential backoff strategy.
2025-04-22 17:16:57 +02:00
If the worker script stays up longer than the last backoff \* 2,
2024-10-03 21:53:12 +02:00
it will not penalize the worker script and restart it again.
However, if the worker script continues to fail with a non-zero exit code in a short period of time
(for example, having a typo in a script), FrankenPHP will crash with the error: `too many consecutive failures` .
2025-06-30 09:38:18 +02:00
The number of consecutive failures can be configured in your [Caddyfile ](config.md#caddyfile-config ) with the `max_consecutive_failures` option:
``` caddyfile
frankenphp {
worker {
# ...
max_consecutive_failures 10
}
}
```
2024-06-05 15:24:16 +02:00
## Superglobals Behavior
[PHP superglobals ](https://www.php.net/manual/en/language.variables.superglobals.php ) (`$_SERVER` , `$_ENV` , `$_GET` ...)
2024-07-27 02:07:52 +04:00
behave as follows:
2024-06-05 15:24:16 +02:00
2025-04-22 17:16:57 +02:00
- before the first call to `frankenphp_handle_request()` , superglobals contain values bound to the worker script itself
- during and after the call to `frankenphp_handle_request()` , superglobals contain values generated from the processed HTTP request, each call to `frankenphp_handle_request()` changes the superglobals values
2024-06-05 15:24:16 +02:00
To access the superglobals of the worker script inside the callback, you must copy them and import the copy in the scope of the callback:
``` php
< ? php
// Copy worker's $_SERVER superglobal before the first call to frankenphp_handle_request()
$workerServer = $_SERVER ;
$handler = static function () use ( $workerServer ) {
var_dump ( $_SERVER ); // Request-bound $_SERVER
var_dump ( $workerServer ); // $_SERVER of the worker script
};
// ...
```