Skip to main content

Running DDEV with FrankenPHP

· 3 min read
Stephan Hochdörfer
Head of IT Business Operations

We use DDEV for almost all of our PHP projects. Also, we use FrankenPHP in our projects, but so far only in production.

Can we make FrankenPHP also run in DDEV to have a more consistent development environment?

For a few versions, DDEV supports the definition of a "generic" web server. This can be a server like node, but also, as in our case, FrankenPHP.

To enable the generic web server, you have to create a file .ddev/config.frankenphp.yaml in your project with the following content:

type: generic
docroot: web
webserver_type: generic

web_extra_daemons:
- name: "frankenphp"
command: "frankenphp php-server --listen 0.0.0.0:80 -v -a"
directory: /var/www/html/public

web_extra_exposed_ports:
- name: "frankenphp"
container_port: 80
http_port: 80
https_port: 443

web_environment:
- SERVER_NAME=ddev-frankenphp.ddev.site:80
- FRANKENPHP_WORKER_CONFIG=watch

This will configure DDEV to run the FrankenPHP process in the /var/www/html/public directory and listen on connections on port 80.

However, this is not enough. Where's the FrankenPHP binary? Well, we need to add that to the DDEV image as follows.

Create a file .ddev/web-build/Dockerfile.frankenphp with the following content to instruct DDEV to install FrankenPHP during building the Docker images:

RUN curl https://frankenphp.dev/install.sh | sh
RUN mv frankenphp /usr/local/bin/
ADD Caddyfile /etc/caddy/Caddyfile

Keep in mind that this command will install FrankenPHP with PHP 8.4 support (as of the time of writing). If you need a different PHP version, you need to compile FrankenPHP yourself.

Since FrankenPHP is built on top of the Caddy web server, we also need to add a Caddy configuration file that will tell FrankenPHP and Caddy how to run.

Create a file .ddev/web-build/Caddyfile with the following content for a basic Caddy setup for a Symfony project:

{
{$CADDY_GLOBAL_OPTIONS}

frankenphp {
{$FRANKENPHP_CONFIG}

worker {
file ./index.php
env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
{$FRANKENPHP_WORKER_CONFIG}
}
}
}

{$CADDY_EXTRA_CONFIG}

{$SERVER_NAME:localhost} {
log {
output stdout
}

root * /var/www/html/public
encode zstd br gzip

{$CADDY_SERVER_EXTRA_DIRECTIVES}

handle_path /assets* {
root * /var/www/html/public/assets
file_server
}

handle_path /build* {
root * /var/www/html/public/build
file_server
}

handle_path /bundles* {
root * /var/www/html/public/bundles
file_server
}

handle_path /media* {
root * /var/www/html/public/media
file_server
}

php_server
}

This configuration will launch FrankenPHP in worker mode and expose the typical Symfony assets directories as is.

With all the configuration files in place, start your DDEV project as always with ddev start and wait a bit until FrankenPHP is installed and up and running.