Problem/Motivation
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 https://github.com/dunglas/frankenphp/releases
# download composer https://getcomposer.org/download/
$ 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
TODO
| Comment | File | Size | Author |
|---|---|---|---|
| #34 | 3437187-nr-bot.txt | 1.57 KB | needs-review-queue-bot |
| #17 | functional.log_.txt | 39.3 KB | nod_ |
Issue fork drupal-3437187
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
nod_Comment #5
nod_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 #3387039: Large placeholders are not processed.
Comment #6
cmlaraIt 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?
Comment #7
nod_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.
Comment #8
mstrelan commentedI 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?
Comment #9
nod_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.
Comment #10
catchYeah 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.
Comment #11
nod_yeah i'll need help for that
Comment #12
dunglas commentedHi,
FrankenPHP author and Caddy maintainer here.
At Les-Tilleuls.coop, we have internal GitLab CI pipelines that use FrankenPHP.
Symfony has a GitHub Actions workflow using it too: https://github.com/symfony/symfony/blob/4b659cbe4c140e5fb73961397e18964b...
This should be straightforward to add. Let me know if I can help doing this!
Comment #13
goz commentedIf it can help pushing this issue, here is some quick perf tests of Drupal with frankenphp : https://www.iosan.fr/en/blog/what-does-frankenphp-and-drupal-have-say-ab...
Summary is :
NGINX+PHP-FPM : ~ 191ms
FrankenPHP : ~ 10ms
Comment #14
nod_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.
Comment #15
nod_Seems there is already an issue on DDEV side: https://github.com/ddev/ddev/issues/5655
Comment #16
catchThe results in https://www.iosan.fr/en/blog/what-does-frankenphp-and-drupal-have-say-ab... 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. Or if it's similar to the n500 in apache bench, you'd expect the calls to be identical.
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.
Comment #17
nod_I'll stop the run here, gotta use my computer for other things.
Attached are the details of the failures.There is a problem with the installer too apparently.
Comment #18
nod_Tried to make a ddev addon but failing so far: https://github.com/theodoreb/ddev-frankenphp-drupal
Problem is with
If anyone has an idea/fix that'd be great, ping me on slack directly to chat
Comment #19
goz commented@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.
Comment #20
catchI had a quick re-read of the post to see if there answer for the discrepancy was in there:
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.
Comment #21
mstrelan commentedThis 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.
Comment #22
nod_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.
Comment #23
andypostFor CI it needs another image a-la https://git.drupalcode.org/project/drupalci_environments/-/tree/dev/php/...
Then it will be much easier to add extra CI run
PS: Let's file issue to project/drupalci_environments
Comment #24
mglamanWould it be possible to hack on this in an experimental GitHub repo using GitHub actions without having to wait for a CI image?
Comment #25
goz commented@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.
Comment #26
nod_Got a working ddev setup:
ddev get theodoreb/ddev-frankenphp-drupal, and it should serve your project with frankenphp, repo here https://github.com/theodoreb/ddev-frankenphp-drupal/tree/main it's using the static build since I just can't seem to make the frankenphp docker image work. Relaunched the tests, much less problems.Comment #27
nod_Opened #3438767: Support FrankenPHP as a webserver
Comment #28
catchI 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.
Comment #29
mstrelan commentedI've been running core's functional tests with FrankenPHP and this Caddyfile on my localhost and getting pretty good results. At first there were many failing tests due to the self signed certificate, but disabling HTTPS resolved those. Currently I'm down to the below list of fails:
Unexpected status code
Unexpected http headers
Unit test fails
Other issues
Comment #30
nod_way less failures than i feared. I have a problem installing a site too so that makes sense the tests fail, thanks for testing that!
I'm nowhere regarding the gitlab ci stuff in #3438767: Support FrankenPHP as a webserver I don't really have time to spend on it and I'm not even sure where to start so any help would be welcome :)
Comment #31
nod_Comment #32
mstrelan commentedI hadn't run FunctionalJavascript tests yet but now that I've started to run some they seem to mostly be passing, only failing on similar issues to the Functional tests.
Started having a look at the first of those fails mentioned in #29. It's expecting a 422 but getting a 403. The interesting thing is that if I repeat the test 10 times it passes 5 five times. So it's definitely capable of sending a 422, but for some reason it's not consistent.
It's possible that some of random files created by
$this->getTestFilesare causing issues and others aren't. Would need to investigate a little further and try to remove the randomness.I've also been thinking about gitlab ci but need some more time. Perhaps as a starting point we could just add the frankenphp static binary to an existing image (like drupalci/php-8.2-cli) and work to build our own image over time
Comment #34
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #35
mstrelan commentedNot sure the Caddyfile supports running in a subdirectory. I have tests running and somewhat passing in Github Actions at https://github.com/mstrelan/drupalci-frankenphp but if I move to a subdirectory it doesn't work. Also struggled to get this working on Drupal Gitlab CI, I think mostly due to file permissions and ownership, but I'll post more about that on #3438767: Support FrankenPHP as a webserver.
Comment #36
andypostbtw PHP 8.3.11 fixing lots of memory leaks so it makes sense to rebuild the image, gonna add drupalci image this weekend based on https://github.com/mstrelan/drupalci-frankenphp
Comment #37
renrhafHi there, coming from here https://github.com/dunglas/frankenphp-drupal/issues/1#issuecomment-23439... just to say that we are using FrankenPHP on a few Drupal projects in production for a few months now. We had some issues related to BigPipe as FrankenPHP has a known issue with PHP Fibers, but appart from that things are running smoothly. We can not use the worker mode yet as the Symfony Runtime is not supported by Drupal as of now.
FrankenPHP got some good performance boosts this summer so the performance tests that were done might be giving different results now.
I'm not sure how to help here to make things go forward.
EDIT: I've created an example repo of what kind of configuration we are using. It is a dockerized Drupal 11 with FrankenPHP. See https://github.com/Renrhaf/drupal11-frankenphp
Comment #38
nod_A blog post with some details/benchmarks would help :)
Comment #40
andypostAs tests running in subdirectory most of them failed, so config should be tuned for CI
Comment #41
el7cosmoslooking at the failed test
this seems like the frankenphp server not started properly.
Comment #42
andypostboth brotli and zstd supported out of box https://externals.io/message/127347
Comment #43
andypostLooks it mostly fixed https://github.com/php/php-src/issues/13648#issuecomment-2900612456
Comment #44
steinmb commentedhttps://thephp.foundation/blog/2025/06/08/php-30/ - 30 years of PHP: FrankenPHP is now part of the PHP organisation
Comment #46
andypostAdded
8.5-ubuntu-frankenphp:devimage also added note about subdir but looks it's not enough yetComment #47
catchThis would allow us to test #3570909: Support early hints for fonts - functionality isn't yet supported in core PHP.