Skip to main content

FrankenPHP + GitLab CI: Release PHP Apps as Binaries

· 3 min read
Boas Falke
PHP Developer

FrankenPHP can embed the source code and assets of PHP applications in a static, self-contained binary. This, paired with GitLab CI, streamlines deployments and releases.

The PHP interpreter currently does not have a production-ready HTTP server integration, so a bridge between the web server and PHP is required. FPM (FastCGI Process Manager) is commonly used to facilitate communication between the web server and the PHP daemon. FPM is compatible with popular web servers such as NGINX, Apache, and Caddy. This setup usually requires two Docker containers, one for the web server and another for PHP/FPM.

Enter FrankenPHP, simplifying this complexity significantly. With FrankenPHP, there's only one service to manage, eliminating the need for external dependencies. This streamlines the development setup, seamlessly integrating into Docker or local environments, and can be easily used in the pipeline, simplifying the entire process.

At the core of FrankenPHP is a custom build of Caddy, specifically designed for PHP. Built-in Go, Caddy harnesses the power of the Go standard library, which includes a highly optimized built-in HTTP server (leveraged by tech giants like Google and Cloudflare). Given that Go is a compiled language capable of calling C code and can be compiled with C files into a standalone binary, it's the perfect foundation for FrankenPHP. Moreover, FrankenPHP introduces advantages like worker mode and supports HTTP Status 103 Early Hints, which was impossible with FPM, for example. You can read more about FrankenPHP on their official website frankenphp.dev.

As a company that relies on GitLab as our Git manager, we've experienced the benefits of its CI/CD is used to automate our applications' build, test, and deployment processes. With FrankenPHP, we can extend the capabilities of our CI/CD pipeline to include the generation of a static standalone binary for our PHP applications. This binary encapsulates the application code, the PHP interpreter, and the Caddy web server, providing a self-contained executable that can be easily deployed to any environment.

To create such a binary, we added a step to our .gitlab-ci.yml that will prepare the app for production by setting the environment variables, removing unnecessary files for prod (e.g., tests), and running FrankenPHP's build-static.sh script. In this example, the final executable will be saved as an artifact, but you can easily upload it to some server.

deployment:build:binary: 
stage: build
image: dunglas/frankenphp:static-builder-1.1.1
rules:
- if: '$CI_COMMIT_TAG =~ /^v?\d+\.\d+\.\d+$/‘
script:
- mkdir -p /go/src/app/dist/app
- git archive HEAD | tar -x -C /go/src/app/dist/app
- cd /go/src/app/dist/app
- echo APP_ENV=prod > .env.local
- echo APP_DEBUG=0 >> .env.local
- echo DATABASE_URL=$DB_URL >> .env.local
- echo MAILER_DSN=$MAILER_DSN >> .env.local
- rm -Rf tests/
- composer install --ignore-platform-reqs --no-dev -a
- composer dump-env prod
- cd /go/src/app/
- EMBED=/go/src/app/dist/app PHP_EXTENSIONS=ctype,iconv /go/src/app/build-static.sh
- ls -al /go/src/app/dist/
- cp /go/src/app/dist/frankenphp-linux-x86_64 /builds/path/to/project/projectName-$CI_COMMIT_TAG-x86_64
- xz --compress /builds/path/to/project/projectName-$CI_COMMIT_TAG-x86_64
after_script:
- echo "BUILD_BINARY_JOB_ID=$CI_JOB_ID" >> job.env
artifacts:
paths: /builds/path/to/project/projectName-$CI_COMMIT_TAG-x86_64.xz
expire_in: never
reports:
dotenv: job.env

In conclusion, FrankenPHP emerges as a game-changer in the PHP ecosystem, offering a streamlined and efficient solution for both development and deployment. By simplifying the setup process and leveraging the strengths of Go and C, developers can focus on what they do best: building exceptional PHP applications. Combined with the right CI/CD setup, distributing these apps becomes easy as well.