For Symfony projects, we recommend using Symfony Docker, the official Symfony Docker setup maintained by FrankenPHP's author. It provides a complete Docker-based environment with FrankenPHP, automatic HTTPS, HTTP/2, HTTP/3, and worker mode support out of the box.
Alternatively, you can run your Symfony projects with FrankenPHP from your local machine:
-
Add the following configuration to a file named
Caddyfilein the root directory of your Symfony project:# The domain name of your server localhost root public/ php_server { # Optional: Enable worker mode for better performance worker ./public/index.php }
See the performance documentation for further optimizations.
-
Start FrankenPHP from the root directory of your Symfony project:
frankenphp run
Since Symfony 7.4, FrankenPHP worker mode is natively supported.
For older versions, install the FrankenPHP package of PHP Runtime:
composer require runtime/frankenphp-symfonyStart your app server by defining the APP_RUNTIME environment variable to use the FrankenPHP Symfony Runtime:
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphpLearn more about the worker mode.
Hot reloading is enabled by default in Symfony Docker.
To use the hot reload feature without Symfony Docker, enable Mercure and add the hot_reload sub-directive to the php_server directive in your Caddyfile:
localhost
mercure {
anonymous
}
root public/
php_server {
hot_reload
worker ./public/index.php
}Then, add the following code to your templates/base.html.twig file:
{% if app.request.server.has('FRANKENPHP_HOT_RELOAD') %}
<meta name="frankenphp-hot-reload:url" content="{{ app.request.server.get('FRANKENPHP_HOT_RELOAD') }}">
<script src="https://cdn.jsdelivr.net/npm/idiomorph"></script>
<script src="https://cdn.jsdelivr.net/npm/frankenphp-hot-reload/+esm" type="module"></script>
{% endif %}Finally, run frankenphp run from the root directory of your Symfony project.
Symfony's AssetMapper component can pre-compress assets with Brotli and Zstandard during deployment. FrankenPHP (through Caddy's file_server) can serve these pre-compressed files directly, avoiding on-the-fly compression overhead.
-
Compile and compress your assets:
php bin/console asset-map:compile -
Update your
Caddyfileto serve pre-compressed assets:localhost @assets path /assets/* file_server @assets { precompressed zstd br gzip } root public/ php_server { worker ./public/index.php }
The precompressed directive tells Caddy to look for pre-compressed versions of the requested file (e.g., app.css.zst, app.css.br) and serve them directly if the client supports it.
FrankenPHP supports efficiently serving large static files after executing PHP code (for access control, statistics, etc.).
Symfony HttpFoundation natively supports this feature.
After configuring your Caddyfile, it will automatically determine the correct value for the X-Accel-Redirect header and add it to the response:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt');
// ...Using FrankenPHP's application embedding feature, it's possible to distribute Symfony apps as standalone binaries.
Follow these steps to prepare and package your Symfony app:
-
Prepare your app:
# Export the project to get rid of .git/, etc mkdir $TMPDIR/my-prepared-app git archive HEAD | tar -x -C $TMPDIR/my-prepared-app cd $TMPDIR/my-prepared-app # Set proper environment variables echo APP_ENV=prod > .env.local echo APP_DEBUG=0 >> .env.local # Remove the tests and other unneeded files to save space # Alternatively, add these files with the export-ignore attribute in your .gitattributes file rm -Rf tests/ # Install the dependencies composer install --ignore-platform-reqs --no-dev -a # Optimize .env composer dump-env prod
-
Create a file named
static-build.Dockerfilein the repository of your app:FROM --platform=linux/amd64 dunglas/frankenphp:static-builder-gnu # If you intend to run the binary on musl-libc systems, use static-builder-musl instead # Copy your app WORKDIR /go/src/app/dist/app COPY . . # Build the static binary WORKDIR /go/src/app/ RUN EMBED=dist/app/ ./build-static.sh
[!CAUTION]
Some
.dockerignorefiles (e.g. default Symfony Docker.dockerignore) will ignore thevendor/directory and.envfiles. Be sure to adjust or remove the.dockerignorefile before the build. -
Build:
docker build -t static-symfony-app -f static-build.Dockerfile . -
Extract the binary:
docker cp $(docker create --name static-symfony-app-tmp static-symfony-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-symfony-app-tmp -
Start the server:
./my-app php-server
Learn more about the options available and how to build binaries for other OSes in the applications embedding documentation.