Add FrankenPHP support
Franken PHP
π What this PR does
- [x] Adds FrankenPHP be offered as a variation
- [ ] Allow the PHP minor version to be selected with FrankenPHP
- [ ] Allow the "autorun" scripts and other custom init scripts to run before bringing up FrankenPHP
- [ ] Allow the
PHP_environment variables from other Server Side Up images to work with FrankenPHP
Create documentation
- [ ] Laravel + FrankenPHP
- [ ] Customizing start up with FrankenPHP
- [ ] Installing custom PHP extensions with FrankenPHP
Checklist
- [ ] Ensure
SSL_MODEworks as other variations - [ ] Ensure custom extensions can be installed like other variations
This would be super cool to have! Great work on this so far!
Deploying serversideup-php with Β
Β Cloudflare Pages
| Latest commit: |
f7cd1d7
|
| Status: | Β β Β Deploy successful! |
| Preview URL: | https://e2aee8a9.serversideup-php.pages.dev |
| Branch Preview URL: | https://280-create-a-frankenphp-vari.serversideup-php.pages.dev |
This would really be a nice addition..
This will be a wonderful addition!
This would be a great feature! π Any updates here?
Not sure how I missed your message @mihai-burduselu-ptt.
I am getting v3.4 updates out the door for the last major optimizations for fpm-nginx. Then it's FrankenPHP time π
This is a great addition to serversideup. Any updates regarding frankenPHP?
I'd like to see FrankenPHP added too.
Thanks for your patience, all! I'm looking for the future of PHP and I really hope FrankenPHP is it.
I've been very busy with Spin and once I get this big release of Spin v3 out the door, then I will be swinging to this.
Exploring FrankenPHP is very important to me and I will definitely be getting this accomplished π―
I am back from holiday break and will be evaluating this for v3.6. I have taken about 3 attempts at this and have paused and deleted each time ππ₯
My challenge is I believe I need to compile FrankenPHP from source so I can maintain the "single source of truth" from the official Docker PHP images. I need to spend more time ensuring this is the way, since I haven't compiled a Go app in my life π
I will keep everyone posted once I have an update but please chime in below if you have any thoughts π
@jaydrogers Not sure if this is feasible? The frankenphp images are based on the official docker images and add their stuff on top, so the php images would still be the source of truth? Itβs additional work to keep the build working, which is done by the frankenphp maintainers anyway. Since itβs based on official docker images, you should be able to do anything you want with the frankenphp images, donβt you?
That being said, I have no deep understanding about what youβre adding, so my opinion might be invalid :sweat_smile:
Thanks for chiming in! Yes, you're totally right -- and I've tried this path. This might be a complicated for a single GitHub issue post, but here is an attempt on simpliying a few critical background parts:
- I use https://www.php.net/releases/active.php as my "source of truth" in get-php-versions.sh
- This builds a
php-versions.ymlfile (base config is here)
If a new version of PHP is made available and FrankenPHP hasn't released that version yet, the build will fail.
This adds two layers of dependencies for all Server Side Up images (if they are FrankenPHP or not):
- Official PHP images must be built
- FrankenPHP must have images built with the EXACT patches that we're trying to use
This is where I hit my head against the wall and decided to take a break and circle back later π
I believe my latest theory was to use Multi-stage builds take the official build PHP image, check out a specific version of FrankenPHP, then compile it following their methods -- but I don't want to re-invent the wheel on compiling either π
Full build process
https://github.com/serversideup/docker-php/blob/4807f9881f4edcb0d70bf16974e4a5f7a1a18698/.github/workflows/service_docker-build-and-publish.yml#L41-L169
@jaydrogers Thanks for the explaination, I definitely see your point. I guess itβs a trade of between path a) or b) where both require extra work then π
If it where up to me to decide, I'd probably add the additional dependency of frankenphp to the automated pipeline: Additionally check the frankenphp releases and only build this flavour of serversideup if the release is available. Again I might be missing something from a lack of knowledge.
Also the frankenphp images apparently are automatically built when a new php image is available: https://frankenphp.dev/docs/docker/#updates
I like @thomasaull's suggestions above. I'm re-evaluating switching back to serversideup/php since things have improved a lot since my last evaluation and I was just considering if there was a frankenphp variation as I personally use caddy in my own image and would like to keep using a caddy variant over nginx or apache.
@jaydrogers - eager to get this going I've decided to give you a hand.
Here's a PR that gets us a bit further down the road. https://github.com/serversideup/docker-php/pull/527
@jaydrogers I've been thinking about your dependency problem and I think you should build off the official frankenphp image rather than build franken yourself.
According to this link: https://frankenphp.dev/docs/docker/#updates
Like yours, the images will be built automatically with each PHP released.
So maybe your images just need to kick off a bit after that. According to the FrankenPHP website 4am UTC?
Although, we don't know how long it might take to appear on dockerhub.
So I think probably you should have the images build independently of each other so the whole build doesn't fail. And then publish on your website which version is available for each. Most of the time I would expect it to be the latest. But sometimes it might be held back. I guess you can have the script email you if there is a problem.
Regarding the images apparently the tags follow this pattern:
dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>
<frankenphp-version> and <php-version> are version numbers of FrankenPHP and PHP respectively, ranging from major (e.g. 1), minor (e.g. 1.2) to patch versions (e.g. 1.2.3).
<os> is either bookworm (for Debian Bookworm) or alpine (for the latest stable version of Alpine).
You can either query the dockerhub api via curl and some scripting magic or use this tool. https://github.com/docker/hub-tool
Script adapted from the internet.
#!/bin/sh
listTags() {
repo="$1"
size="${2:-25}"
page="${3:-1}"
if [ -z "$repo" ]; then
echo "Usage: listTags <repoName> [size] [pageIndex]" >&2
return 1
fi
#curl "https://hub.docker.com/v2/repositories/${repo}/tags?page=${page}&page_size=${size}"
curl -s "https://hub.docker.com/v2/repositories/${repo}/tags?page=${page}&page_size=${size}" 2>/dev/null | jq -r '.results[].name' | sort
}
listTags dunglas/frankenphp 10 1
Just some thoughts. Very happy to help out. If you want some more in depth discussion or a sounding board on it let me know. I haven't delved very deep into your build process as yet.
Hi, any update on this?
Hi, any update on this?
I think @jaydrogers has been quite busy. So have I. It's certainly not forgotten. I will be needing this soon also.
Hi, @jaydrogers . Any roadmap or updates on this? π
Haven't forgotten. Working on a big update that's related to this, then I will swing back.
Stay tuned ππ
Just checking in on this, thanks!
I'm out of the office this week, but I plan on doing serversideup/php items once I get back next week π
Do you need some help on this? I have some time next week to pick this up if needed.
Huge update π₯³
Thank you so much for your patience, all!
Just wanted to let you know that supporting FrankenPHP is very important to serversideup/php. I finally had the time to sit down and deep into this. Here's where I'm at:
FrankenPHP is now compiled by source π€
I never compiled a Go app in my life, but I was able to go through the official Dockerfiles and understand what's going on. Good news is I'm able to get Alpine and Debian to build from a single Dockerfile, making it pretty easy to maintain.
π Extra features compared to official FrankenPHP
Since we're able to compile the binary ourselves, it opens up a ton of possibility:
Compile with any OS and any PHP version
I spent a crapload of time refactoring my "php-versions.yml" file. This allows us to set advanced rules like:
- Choosing what PHP versions a variation supports
- Setting what operating systems each PHP version supports
This means that we can run FrankenPHP on a version pinned operating system (alpine3.22, alpine3.21, etc). Oh, and don't worry -- I also have "family level" tags so alpine will be tagged on the latest version of alpine or but it still allows you to version pin if you want. π€ͺ
We'll actually be able to run FrankenPHP on more operating systems compared to their original offering.
Unprivileged by default
Running as the www-data user by default is very important for security. Just like our other variations, we will run on 8080 or8443 by default. I am hoping I don't run into any issues with this, but so far my tests are coming back well.
All serversideup scripts will be included
One of my favorite things about the serversideup/php images are the automation and helper scripts that are provided. All of these scripts will be inside of and fully supported in the serversideup/php:frankenphp image π
π Next Steps
There's still A LOT left for me to do. The first comment will continuously be updated with my task status. I want to make sure FrankenPHP is offering the same features as we do in our fpm-nginx image. This will also be my first time writing a Caddyfile (sorry, don't hate -- I've always used Traefik π
) so please bear with me as I learn this process.
I pushed up my code in case someone wants to start looking at my approach so far. If you're interested in helping out, just please comment below with your proposed changes before rampaging on a PR. There's a lot of moving parts in this release, so I just want to respect your time. I need to fully understand the proposed change before it can be merged.
Here's a PHP info page below to show proof that I'm not bluffing π
Looking forward to getting this out the door! Thanks again for your patience π
@jaydrogers, will this variation still require changing permissions during the build phase?
Yup, it will be unprivileged by default (for best security), but you can add "USER root" if you need to gain root permissions.
Also, remember, you can use "docker exec -u root" if you need to get root access to an unprivileged container for debugging π€
π FrankenPHP is ready for testing

π How to test
See the top comment on this PR for latest updates, resources, and progress.
π Have an issue?
- Open an issue on GitHub that is simple and reproducible.
- Make sure it follows our "Guide On How To Get Help".
- Clearly indicate it relates to the new 4.0 testing
Thanks for your patience and encouragement, y'all βοΈ
A quick chat with GPT5 said this on Caddyfile. @jaydrogers
Key takeaway for me is everything is good, but then the dot env side of things comes off like it needs double checking in terms of security atleast
Here is what it suggested
(security) {
@rejected path *.bak *.conf *.dist *.fla *.ini *.inc *.inci *.log *.orig *.psd *.sh *.sql *.swo *.swp *.swop
# Explicit root-level & nested dotfiles and common secrets
@secrets path .env .env.* .git .git/* .gitignore .gitattributes composer.* package-lock.json yarn.lock pnpm-lock.yaml
respond @rejected 403
respond @secrets 403
header {
defer
X-Frame-Options "SAMEORIGIN" # keep for legacy UAs
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
-X-Powered-By
# Modern hardening (tune to your app)
Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requests"
Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=()"
Cross-Origin-Resource-Policy "same-site"
Cross-Origin-Opener-Policy "same-origin"
}
}
(security-https) {
header {
defer
Strict-Transport-Security "max-age=31536000; includeSubDomains"
# add "; preload" ONLY if you control all subdomains and plan to submit to the preload list
}
}
And also hide at the file server level:
(app-common) {
root * {$CADDY_APP_PUBLIC_PATH:/var/www/html/public}
encode zstd br gzip
# Access log level import stays
import log-level/address/{$LOG_OUTPUT_LEVEL:info}.caddyfile
@health { path /healthcheck }
respond @health "OK" 200
log_skip @health
php_server {
{$CADDY_PHP_SERVER_OPTIONS}
}
# Never serve these if they slip past routing
file_server {
hide .env .env.* .git .git/* .gitignore .gitattributes composer.* package-lock.json yarn.lock pnpm-lock.yaml
}
import performance
import security
{$CADDY_SERVER_EXTRA_DIRECTIVES}
}
I'm no caddy expert. Just trying to help if it's any help at all to get this out as safely and soon as possible.
Thanks
I'm definitely open for discussion on how to improve the Caddyfile (because I just learned Caddy this week), but I might yield the proposals to improvements by humans with real experience.
I am all for using AI as an assistant, but extremely hesitant to let it take the wheel on improvements (because I could respond with "Are you sure?" and it would likely 2nd guess itself anyways π€ͺ)
If anyone else can help decipher these proposals, I am all ears π
Been using this alpha for the last 40 mins and it was almost a drop in replacement for fpm-nginx. The only change I had to make was removing an explicit nginx permissions call leftover in my Dockerfile.
I'm sure there are probably minor tweaks that will be made before release, but this seems pretty solid so far. Great work @jaydrogers