Skip to content

Conversation

jaydrogers
Copy link
Member

@jaydrogers jaydrogers commented Feb 8, 2024

4.0 Release: Say hello to FrankenPHP 👋

🚀 What this PR does

This PR is our home base for testing our new 4.0 release. This adds the highly anticipated variation of FrankenPHP and further optimizes serversideup/php to be highly optimized for deploying and maintaining Laravel applications.

🫵 WE NEED YOU: Help us test test this release

Important

Please keep reading the notes in this post before cowboy coding and throwing this into production 🤠

  1. This PR is in an "alpha" state and could change at anytime
  2. The images are located on a special "development" repository php-dev (not php)
  3. There are more changes than just adding FrankenPHP. Read the entire post for what's new

View Test Images on Dockerhub →

Anything tagged with 283- will reference this PR and should be tested. Notice we're testing on php-dev (not php).

serversideup/php-dev:283-*

🐛 Reporting Issues

👨‍🔬 What to test

We really need the community's help on testing these images as we progress towards stable.

1️⃣ All variations: Laravel Automations

There were huge improvements made to the 50-laravel-automations.sh script.

  1. Does the script still work after upgrading an existing FPM-NGINX image?
  2. Is the script behaving well and giving you the results you anticipate?

2️⃣ FrankenPHP: Caddyfile structure

  1. Do you like the structure of the Caddyfile?
  2. Do you find the script flexible and easy to expand?
  3. Do the performance defaults make sense and follow best practices?
  4. Do the security defaults make sense and follow best practices?
  5. Are you getting the RealIP when the container is behind a CloudFlare proxy?
  6. How is your experience with logging? Does the LOG_LEVEL_OUTPUT help you out? Are the logs too noisy?

3️⃣ Test start up scripts

We made improvements to the entrypoint script.

  1. Are you getting any weird behaivor with old scripts?
  2. Do you like how we execute things in a subshell to isolate scripts and have a more natural developer experience?

🌎 Latest Documentation

Use the link below to reference the latest documentation (it will automatically update as we keep improving the docs).
View the latest documentation →

⚡️ What's new

🧟‍♂️ FrankenPHP variations now added

The highly anticipated release of FrankenPHP is now available. These images come with many enhancements compared to the official FrankenPHP images.

More operating system variations

We are able to compile FrankenPHP by source, which allows us to open up support for many operating systems.

How tagging works
There's more to it, but in general the primary principle is:

{php-minor-version}-{variation}-{os-version}

This means we're offering FrankenPHP with the following operating systems:

  1. trixie: Debian Trixie (13)
  2. bookworm: Debian Bookworm (12)
  3. alpine3.22: Alpine 3.22
  4. alpine3.21: Alpine 3.21

Images are unprivileged by default

For best security practices, we're running things as www-data. This dramatically reduces your security footprint when running PHP in production. Because of this, we're listening on 8080 (HTTP) and `8443 (HTTPS). This follows the same design pattern as our other images.

Extremely flexible and production-grade Caddyfile by default

The default FrankenPHP Caddyfile gives you enough to get started, but we spent a ton of time making sure that we're shipping production-grade and secure configurations by default. This includes:

  1. Native CloudFlare support with trusted IP addresses
  2. Performance and caching rules made available by default
  3. Security headers included by default
  4. Flexible and powerful logging defaults
  5. Simple and intelligent self-signed certificate generation (but still allowing you to use Let's Encrypt if you wanted)

Designed for mass-scale production deployments

It's almost unbelievable and amazing how well FrankenPHP works with Caddy as a proxy. This tight integration allows you to do magical things like deploy trusted SSLs with Let's Encrypt. The only problem is, you probably have something else serving SSL termination and you most likely would not use that feature in a single container.

Our approach is "orchestrator first", meaning the image is designed for mass-scale in mind.

This means we're shipping the image assuming that you're doing TLS termination elsewhere. This makes it easier for you to scale and perform zero-downtime deployments:

flowchart TD
    A["Reverse Proxy 
    (Not FrankenPHP)"] -->C{Container Service}
    C -->|STOP| D[MyApp:v1]
    C -->|START| E[MyApp:v2]
Loading

Flexible environment configuration

Just like the experience with our other PHP variations, we also have things like SSL_MODE, LOG_OUTPUT_LEVEL, changing PHP INI settings with environment variables, all our helper scripts for changing permissions, etc. that make it a breeze for you to customize how the PHP image behaves.

🌎 New Environment Variables

The following environment variables are now available:

Environment Variable Default Authored By
AUTORUN_DEBUG false @jaydrogers
AUTORUN_LARAVEL_SEED false @DarkGhostHunter
AUTORUN_LARAVEL_MIGRATION_SKIP_DB_CHECK false @jaydrogers
PHP_FPM_PM_MAX_REQUESTS 0 @ifaridjalilov, @thueske
PHP_MAX_INPUT_VARS 1000 @RadeJR
PHP_OPCACHE_FORCE_RESTART_TIMEOUT 180 @aSeriousDeveloper, @jaydrogers
PHP_OPCACHE_JIT off @aSeriousDeveloper, @jaydrogers
PHP_OPCACHE_JIT_BUFFER_SIZE 0 @aSeriousDeveloper, @jaydrogers
PHP_OPCACHE_SAVE_COMMENTS 1 @aSeriousDeveloper, @jaydrogers
PHP_OPCACHE_VALIDATE_TIMESTAMPS 1 @aSeriousDeveloper, @jaydrogers

🤩 New Features

  • Added php artisan optimize support to the Laravel Automations script
  • Added php artisan db:seed support to the Laravel Automations script
  • Added AUTORUN_DEBUG environment variable to help diagnose issues with Laravel Automations
  • Refactored entire Laravel Automations script for better flexbility and user experience
  • Changed approach to executing entrypoint.d scripts so we can gracefully handle exit 0 in a entrypoint script
  • Re-designed container start up info script

🐛 Bug Fixes

✅ Jay's Checklist

These are notes to myself so I can remember where I left off as I start merging more things in:

Development

  • Adds FrankenPHP be offered as a variation
  • Test LOG_LEVEL_OUTPUT
  • Convert Caddyfile to work like our fpm-nginx image
  • Allow the "autorun" scripts and other custom init scripts to run before bringing up FrankenPHP
  • Allow the PHP minor version to be selected with FrankenPHP
  • Allow the PHP_ environment variables from other Server Side Up images to work with FrankenPHP
  • Ensure set-id and set-filepermissions scripts work well
  • Create native healthchecks
  • Ensure CloudFlare Real IPs work

Documentation

  • Go through all pages and see if it is appropriate to update FrankenPHP
  • Add FrankenPHP on the homepage
  • Document the healthchecks for FrankenPHP

@jaydrogers jaydrogers linked an issue Feb 8, 2024 that may be closed by this pull request
@jaydrogers jaydrogers marked this pull request as draft February 8, 2024 00:01
@Sammyjo20
Copy link

This would be super cool to have! Great work on this so far!

Copy link

cloudflare-workers-and-pages bot commented May 13, 2024

Deploying serversideup-php with  Cloudflare Pages  Cloudflare Pages

Latest commit: a3d592c
Status: ✅  Deploy successful!
Preview URL: https://1edf9384.serversideup-php.pages.dev
Branch Preview URL: https://280-create-a-frankenphp-vari.serversideup-php.pages.dev

View logs

@GunniBusch
Copy link

This would really be a nice addition..

@thinkstylestudio
Copy link

This will be a wonderful addition!

@mihai-burduselu-ptt
Copy link

mihai-burduselu-ptt commented Aug 27, 2024

This would be a great feature! 🚀
Any updates here?

@jaydrogers
Copy link
Member Author

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 👍

image

@brufdev
Copy link

brufdev commented Aug 23, 2025

@jaydrogers, will this variation still require changing permissions during the build phase?

@jaydrogers
Copy link
Member Author

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 🤓

…ence (#527)

* update franken versions and create an initial caddyfile

* Caddy likes tabs over spaces

* Set default env vars in the dockerfile, create webroot

* update dockerfile and add healthcheck

* update caddyfile. https not yet working

* Refactor dependency installs to support "docker-php-serversideup-set-file-permissions" across OS's

* Fix typo in SSL certificate path in 10-init-unit.sh script

* Add /etc/ssl/private directory to file permissions in docker-php-serversideup-set-file-permissions script

* Add script to generate self-signed SSL certificate and key for container

* Update Dockerfile to copy entrypoint scripts to /etc/entrypoint.d/ for improved container initialization

* Remove reference file

* Add Caddy configuration options and SSL mode handling for FrankenPHP

- Introduced new environment variables for Caddy configuration in the Dockerfile, including options for admin interface, public path, and HTTPS settings.
- Updated the Caddyfile to support different SSL modes (off, mixed, full) with corresponding configurations.
- Enhanced the SSL generation script to skip certificate generation when SSL mode is off.
- Added new Caddyfile imports for structured configuration management.
- Improved logging and health check responses in the Caddyfile.

* Enhance Caddyfile configuration for FrankenPHP with security and caching improvements

- Added trusted proxies configuration to support Docker and Cloudflare.
- Introduced client IP header handling for better client identification.
- Updated static file handling with improved caching headers and logging for specific files.
- Implemented security headers to mitigate common web vulnerabilities.
- Imported security settings into SSL mode configurations for full and mixed modes.

* Add logging configuration options for Caddy in FrankenPHP

- Introduced new environment variables for log formatting and log levels.
- Updated the Caddyfile to import log level configurations for both global and address-specific logging.
- Created separate Caddyfile configurations for various log levels (debug, info, warn, error, crit, alert, emerg) to enhance logging granularity.
- Adjusted Dockerfile to include new log-level configuration files in the container.

---------

Co-authored-by: Matt Hook <[email protected]>
Co-authored-by: Jay Rogers <[email protected]>
Co-authored-by: Jay Rogers <[email protected]>
@jaydrogers jaydrogers changed the base branch from release/v3.6 to main August 28, 2025 19:33
@jaydrogers jaydrogers changed the title Add FrankenPHP support Release 4.0: Add FrankenPHP & more Laravel optmizations Aug 28, 2025
@jaydrogers jaydrogers added the ⚡️ Enhancement Items that are new features requested to be added. label Aug 28, 2025
@jaydrogers jaydrogers marked this pull request as ready for review August 28, 2025 19:51
@jaydrogers
Copy link
Member Author

🚀 FrankenPHP is ready for testing

👇 How to test

See the top comment on this PR for latest updates, resources, and progress.

🐛 Have an issue?

Thanks for your patience and encouragement, y'all ✌️

@jaydrogers jaydrogers mentioned this pull request Aug 28, 2025
8 tasks
@opheus2
Copy link

opheus2 commented Aug 28, 2025

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

image

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

@jaydrogers
Copy link
Member Author

jaydrogers commented Aug 28, 2025

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 🙏

@tomschlick
Copy link
Contributor

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

@jaydrogers
Copy link
Member Author

Thanks @tomschlick! That means a lot hearing it from someone experienced as you 😃

Keep me posted how things run in the meantime. I might be merging a few smaller PRs next week 👍

@alfredcantrell
Copy link

Been running the image for a little bit and everything seems ok from my side! Idle memory usage has increased compared to unit, but idle CPU usage has decreased, performance has remained similar as well!

Note: This probably isn't a true comparison, so not sure if this is actually useful data

Unit

Thread Stats Avg Stdev Max +/- Stdev
Latency 24.53ms 4.92ms 246.98ms 95.71%
Req/Sec 1.03k 65.99 1.14k 86.93%
Total
Requests 40866
Duration 20.01s
Data Read 29.51MB

Franken Alpine

Thread Stats Avg Stdev Max +/- Stdev
Latency 23.91ms 4.69ms 130.49ms 95.67%
Req/Sec 1.06k 67.87 1.14k 92.96%
Total
Requests 41956
Duration 20.01s
Data Read 35.16MB

@tony-stark-eth
Copy link

Been running the image for a little bit and everything seems ok from my side! Idle memory usage has increased compared to unit, but idle CPU usage has decreased, performance has remained similar as well!

Note: This probably isn't a true comparison, so not sure if this is actually useful data

Unit

Thread Stats Avg Stdev Max +/- Stdev
Latency 24.53ms 4.92ms 246.98ms 95.71%
Req/Sec 1.03k 65.99 1.14k 86.93%
Total
Requests 40866
Duration 20.01s
Data Read 29.51MB
Franken Alpine

Thread Stats Avg Stdev Max +/- Stdev
Latency 23.91ms 4.69ms 130.49ms 95.67%
Req/Sec 1.06k 67.87 1.14k 92.96%
Total
Requests 41956
Duration 20.01s
Data Read 35.16MB

The real magic should happen with the worker mode of frankenphp, though not every app will support out of the box. I guess this example it without it?

@jaydrogers
Copy link
Member Author

jaydrogers commented Aug 29, 2025

If someone wants to give worker mode a whirl and keep me posted of your results, I would be very interested. I left a section in the Caddyfile called $FRANKENPHP_CONFIG if you need to set options there.

frankenphp {
{$FRANKENPHP_CONFIG}
}

Official Docs:
https://frankenphp.dev/docs/worker/

…rameter for specifying the output file path. Default output path remains unchanged. Improved error handling for missing output file path argument.
…e input file path. Improved error handling for missing input file argument. Updated usage documentation accordingly.
… additional PHP variations (fpm-apache and fpm-nginx) for improved file permission management across different server configurations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⚡️ Enhancement Items that are new features requested to be added.
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

Create a FrankenPHP variation