Add Caddyfile configuration

Created on 31 March 2024, 13 days ago
Updated 5 April 2024, 7 days ago


We should introduce a Caddyfile configuration to enable Drupal to be served by caddy. It would also make it possible to use FrankenPHP easily, and benefit from all it's features such as, server sent events, early hints to load assets faster, etc.

Later, once #2218651: [meta] Make Drupal compatible with persistent app servers like ReactPHP, PHP-PM, PHPFastCGI, FrankenPHP, Swoole β†’ is in a good state, we can enable the worker mode of FrankenPHP and improve performance. The end goal would be to recommend FrankenPHP as the way to deploy Drupal on production.

We can also make use of FrankenPHP static binary to provide a PHP-cli and make it possible to use and serve Drupal (including composer and drush) from an environement that does not have PHP installed. From what I tried the following works:

# download the relevant binary for you system from
# download composer
$ frankenphp php-cli ~/opt/bin/composer create-project drupal/recommended-project look_no_hands
$ cd look_no_hands
$ frankenphp php-cli ~/opt/bin/composer require drush/drush
$ cd web
$ frankenphp run

You have a Drupal up and running on a system without PHP installed, you used composer and drush works. While this is out of scope for this issue, it is to give an idea about how it can be used and the relevance of adding a Caddyfile to core. There is a docker way of doing all this, but I wanted to highlight the simplest way it can be made to work.

Proposed resolution

Add a Caddyfile to core, next to .htaccess, taking inspiration from the frankenphp-drupal config.

Remaining tasks

Release notes snippet


πŸ“Œ Task

Needs review


11.0 πŸ”₯

BaseΒ  β†’

Last updated about 7 hours ago

Created by

πŸ‡«πŸ‡·France nod_ Lille

Live updates comments and jobs are added and updated live.
  • Performance

    It affects performance. It is often combined with the Needs profiling tag.

Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @nod_
  • πŸ‡«πŸ‡·France nod_ Lille
  • Merge request !7256Resolve #3437187 "Add caddyfile configuration" β†’ (Open) created by nod_
  • Merge request !7276Draft: Resolve #3437187 "Frankenphp features" β†’ (Open) created by nod_
  • Status changed to Needs review 11 days ago
  • πŸ‡«πŸ‡·France nod_ Lille

    I've added a MR with a proof of concept showing how we can use the early hints feature to make JS/CSS load faster, improving performance.
    The assets are in the headers of the first response, the browser does not need to parse the HTML to start loading assets, making the page show up faster. It's pretty significant when throttling the browser on a slow 3G connection.

    Another improvement we could make is using server sent events (using the build-in mercure hub) to replace some of the bigpipe implementation, that could help simplify the code and workarounds made for things like πŸ› Large placeholders are not processed RTBC .

  • πŸ‡ΊπŸ‡ΈUnited States cmlara

    It appears this might conflict with the new standard from the Core team and Drupal Security Team attempting to slim down on the amount of security surface area by removing supported servers types.

    This might need to instead just be documentation only?

  • πŸ‡«πŸ‡·France nod_ Lille

    My personal end goal is to get everyone to use FrankenPHP in worker mode because I believe it'll make it cheaper to setup and host Drupal as well as be more performant.

    It could address the "Server included" and the "Drupal.exe" use cases of Can Drupal scale down?. The built in mercure server could be used and relied on by contrib to provide more real-time features and make modules such as DrupalChat β†’ performant by default.

    I do not think we should go the documentation only route. As a first step why not, but there is more to it than that. If we make the work to have Drupal compatible with "application servers" we should get something more than "just" performance for it.

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    I think part of dropping IIS and Windows support is that we can't test on it. Is it possible, or is there an existing issue, where we can run caddy in gitlab ci?

  • πŸ‡«πŸ‡·France nod_ Lille

    I would be surprised if we couldn't, it's linux friendly, there is a static binary that works everywhere or a docker image for it.

  • πŸ‡¬πŸ‡§United Kingdom catch

    Yeah I came here to post the same thing as #8, if we can add a frankenphp gitlab environmnet and run that on commit, that would ensure that what we're providing will actually work when people try to use it.

  • πŸ‡«πŸ‡·France nod_ Lille

    yeah i'll need help for that

  • πŸ‡«πŸ‡·France dunglas


    FrankenPHP author and Caddy maintainer here.

    At, we have internal GitLab CI pipelines that use FrankenPHP.
    Symfony has a GitHub Actions workflow using it too:

    This should be straightforward to add. Let me know if I can help doing this!

  • πŸ‡«πŸ‡·France GoZ

    If it can help pushing this issue, here is some quick perf tests of Drupal with frankenphp :

    Summary is :
    NGINX+PHP-FPM : ~ 191ms
    FrankenPHP : ~ 10ms

  • πŸ‡«πŸ‡·France nod_ Lille

    Tried to look into making testbot/ddev use frankenphp. We need someone who know those things to help out, I don't have the time to figure it out, I can help with how to configure frankenphp (to some extend) but the rest is above my head.

    I've been running the functional tests (not the JS ones yet) with the static frankenphp version. There are failures due to different handling of headers by caddy, something with authorization headers etc. It's going to take a while to run all this locally.

  • πŸ‡«πŸ‡·France nod_ Lille

    Seems there is already an issue on DDEV side:

  • πŸ‡¬πŸ‡§United Kingdom catch

    The results in are a bit confusing - if frankenphp is responding in about 1/20th the time, why is it only serving 565 calls vs. 483? You would expect it to be able to serve a much higher number of calls in the same time.

    It's also not clear if this is testing anonymous users or not, average response time of 191ms with Drupal's internal page cache would suggest a misconfiguration somewhere, it should be <10ms if page caching is working. If it's auth users or with page caching disabled, that would make more sense, but I'd still want to know why the number of requests served doesn't differ as much as the response time.

  • πŸ‡«πŸ‡·France nod_ Lille

    I'll stop the run here, gotta use my computer for other things.

    $ ../vendor/bin/phpunit --testsuite functional
    PHPUnit 9.6.15 by Sebastian Bergmann and contributors.
    Runtime:       PHP 8.3.4
    Configuration: /home/theodore/DEV/DRUPAL/drupal/core/phpunit.xml
    FSSSFSSSFSSSFSSSFSSSFSSSFF.......................E...........  183 / 4439 (  4%)
    .............E..EEEE......E...E.E..EEEEE.E.......E..E..E.E.E.  244 / 4439 (  5%)
    ..EE.EEE.EEEE.EEE..EEE.......................................  305 / 4439 (  6%)
    ...........................EEF...............................  366 / 4439 (  8%)
    .....E........FSSSFSSSFSSSFSSSFSSSFSSS........E.E..E.....EE..  427 / 4439 (  9%)
    ...EE.........EEE.....FSSSFSSSFSSSFSSSFSSSFSSS...EE..E.......  488 / 4439 ( 10%)
    ...E..........EE...E..........E......SSFSSFFSSSFSSSSSFSSFFSSS  610 / 4439 ( 13%)
    FSSSSSFSSFF.FFFFFSSSFSSSF.FFFFSSFFFF.........................  671 / 4439 ( 15%)
    ..................E.....E.E......FE..F...E...................  732 / 4439 ( 16%)

    Attached are the details of the failures.There is a problem with the installer too apparently.

  • πŸ‡«πŸ‡·France nod_ Lille

    Tried to make a ddev addon but failing so far:
    Problem is with

    Error: loading initial config: loading new config: loading http app module: provision http: loading pki app module: provision pki: provisioning CA 'local': generating root: saving root certificate: mkdir /data/caddy/pki: permission denied

    If anyone has an idea/fix that'd be great, ping me on slack directly to chat

  • πŸ‡«πŸ‡·France GoZ

    @catch i agree with you, the number of calls does not reflect the difference of response time.
    As explained in blog post, both tests are done with minimum configuration :

    - a fresh drupal installation with default umami profile.
    - no differences between the two jmeter scenarios

    I don't have dedicated servers to make real tests, but i can at least confirm i tested in the same way both stacks.
    I'm not expert about jmeter, so if someone want to improve those tests, feel free to contribute !

    Tests have been done with anonymous users, requesting home page.

  • πŸ‡¬πŸ‡§United Kingdom catch

    I had a quick re-read of the post to see if there answer for the discrepancy was in there:

    and searched for the "Vegan" string on performance tests for 600 seconds with an HTTP call (no HTTPS).

    This might explain things - say nginx could serve 30 x 1 second requests per second, that's 18,000 requests in 600 seconds.

    But if frankenphp can only serve 10 x 100ms requests per second, that'd be 6,000 even if each individual request is ten times as fast.

    Possible changes to make to the load test:
    1. Create a regular authenticated user in Umami, and get jmeter to add their session cookie to the request (I haven't used jmeter for years now but there must be docs for this). This means a lot more PHP to execute, making any network traffic variation less of a variable.

    2. Go down to single concurrency, so it's how many end-to-end requests can be done in the 600s, this will eliminate any concurrency limits that might be in place in server config and a lot of hardware/resource bottlenecks too.

  • πŸ‡¦πŸ‡ΊAustralia mstrelan

    This issue feels it should have a meta for discussing the merits of frankenphp and another issue for gitlab ci, so we can focus this issue on the caddyfile.

  • πŸ‡«πŸ‡·France nod_ Lille

    It's going to take time for all this, I enjoy a bit of chaos at the start :) I don't want to split things up too early and dissipate the interest.

    Technically it should go to the ideas queue to discuss merits, then a plan and all the proper issues in the proper places set up. Just wanted to shake things up and see if some people already got it without all the detailed explanations, once we have something more solid it'll be time to go into more details about what that all means. Just getting this in drupalci will be a challenge, ddev is more realistic and we can find the problems there first. If we know there is a deal-breaker in how this works there is no point in spending DA time getting the CI sorted out.

  • πŸ‡«πŸ‡·France andypost

    For CI it needs another image a-la
    Then it will be much easier to add extra CI run

  • πŸ‡ΊπŸ‡ΈUnited States mglaman WI, USA

    Would it be possible to hack on this in an experimental GitHub repo using GitHub actions without having to wait for a CI image?

  • πŸ‡«πŸ‡·France GoZ

    @catch i figured out where i was wrong.

    I misconfigured my Drupal settings for the nginx+php-fpm (no cache).

    I update the blog post to fix this.

    I make tests again as anonymous with all caches disabled (Drupal + twig) to enforce calculation.
    And another as anonymous with all caches enabled.

    Jmeter has been configured to launch only one thread at time, with no delay between two threads.

    Summary is :

    No cache :
    NGINX+PHP-FPM : ~ 202ms
    FrankenPHP : ~ 226ms

    Cache enabled :
    NGINX+PHP-FPM : ~ 5.43ms
    FrankenPHP : ~ 6.36ms

    Otherwise, something is still wrong since there is a big difference between the time with/without cache and the number of request with/without cache.

  • πŸ‡«πŸ‡·France nod_ Lille

    Got a working ddev setup:

    ddev get theodoreb/ddev-frankenphp-drupal, and it should serve your project with frankenphp, repo here it's using the static build since I just seem to make the frankenphp docker image work. Relaunched the tests, much less problems.

  • πŸ‡¬πŸ‡§United Kingdom catch

    I think you mentioned some of the core test failures are due to different headers being sent by frankenphp vs. apache, I think I've seen this with nginx and ddev too at least a couple of times too.

Production build 0.62.1