Membangun FrankenPHP sesuai kebutuhan: menambahkan ekstensi PHP ke image Docker, memahami build flags (--enable-embed, --enable-zts, --disable-zend-signals), menyusun binary khusus dengan static-php-cli, serta menambahkan modul Caddy lewat xcaddy.

Selama 20 episode, kita memakai binary FrankenPHP yang sudah jadi. Episode ini membuka kotak hitam: bagaimana membangun FrankenPHP sendiri — dan kapan perlu melakukannya. FrankenPHP bukan sekadar binary; ia adalah kombinasi PHP (mode embedded) dengan Caddy (Go), dan keduanya bisa dikustomisasi: ekstensi PHP, modul Caddy, atau bahkan binary statis dari sumber.
Mengapa penting? Tidak semua kebutuhan terpenuhi oleh image resmi. Ekstensi proprietary, modul Caddy khusus, atau distribusi binary statis untuk alpine yang sangat kecil — semuanya menuntut kemampuan build. Dan memahami proses build juga menjelaskan mengapa konfigurasi PHP di FrankenPHP terasa berbeda dari PHP-FPM (mode embed menuntut ZTS).
FrankenPHP menyematkan PHP ke dalam proses (mode embedded), sehingga PHP harus dikompilasi dengan:
--enable-embed — izinkan PHP dipanggil sebagai library (embedded).--enable-zts — Thread Safety (ZTS): PHP dapat berjalan di banyak thread dalam satu proses. Ini wajib untuk mode worker.--disable-zend-signals — menghindari konflik signal handler dengan Go runtime; di beberapa versi PHP/Zend diperlukan agar tidak saling menimpa sinyal.Tanpa ZTS, mode worker tidak mungkin: worker FrankenPHP adalah thread yang menjalankan runtime PHP, dan runtime PHP membutuhkan ZTS untuk aman dalam multithreading.
Cara termudah menambah ekstensi — tanpa build FrankenPHP dari nol — adalah image resmi dengan install-php-extensions:
FROM dunglas/frankenphp:1.12.7-php8.5-bookworm
RUN install-php-extensions \
pdo_mysql \
redis \
gd \
exif \
zip
USER frankenphp
WORKDIR /app
COPY --chown=frankenphp:frankenphp . .install-php-extensions memilih versi yang kompatibel dengan versi PHP image, menangani dependensi sistem, dan mengaktifkan ekstensi sekaligus. Ini adalah jalur yang direkomendasikan untuk 90% kebutuhan ekstensi.
Kadang ekstensi sudah terpasang tapi belum aktif. Aktifkan via php_ini di Caddyfile:
{
frankenphp {
php_ini extension=sodium
php_ini extension=pcntl
}
}
localhost {
root * public/
php_server
}Untuk mengendalikan konfigurasi PHP sepenuhnya, build dari sumber:
git clone https://github.com/php/frankenphp.git
cd frankenphp
./build-static --help # lihat opsi build
./build-static 8.5.15 --with-xdebug # contoh: PHP 8.5.15 + Xdebugbuild-static menghasilkan binary statis yang menggabungkan PHP dan Caddy. Setelah build, gunakan binary tersebut langsung:
./frankenphp run --config Caddyfile--with extensionsFlag --with-* menambahkan ekstensi ke build statis:
./build-static 8.5.15 \
--with-zlib --with-zstd --with-curl --with-openssl \
--with-pdo-pgsql --with-pdo-mysql --with-redisDaftar lengkap opsi tersedia di ./build-static --help. Build dari sumber membutuhkan toolchain lengkap (compiler, autotools) — di CI, gunakan environment yang sesuai.
Untuk distribusi binary kecil yang tidak bergantung pada library sistem (bagus untuk alpine scratch atau edge devices), static-php-cli adalah alat yang pas. Ia mengkompilasi PHP statis, lalu frankenphp bisa dibangun di atasnya:
composer create-project crazywhalecc/static-php-cli spc
cd spc
# build PHP statis dengan ekstensi
./bin/spc download --with-php="8.5" --with-extension="redis,pdo_mysql,mbstring"
./bin/spc build --with-php="8.5" --with-extension="redis,pdo_mysql,mbstring" --enable-frankenphpHasilnya binary frankenphp tunggal yang membawa PHP + semua ekstensi — tanpa ketergantungan glibc.
Tip
Binary statis (musl) sangat menguntungkan untuk image berbasis Alpine: ukuran image bisa turun drastis. Ingat trade-off kinerja memori vs glibc dari episode 17 — pilih sesuai profil beban kalian.
xcaddyFrankenPHP adalah Caddy dengan modul bawaan (php_server, frankenphp). Untuk menambahkan modul Caddy lain (misal WAF custom, penyimpanan session, dll), gunakan xcaddy:
git clone https://github.com/caddyserver/xcaddy
cd xcaddy
go build
# build FrankenPHP + modul tambahan
./xcaddy build --with github.com/php/frankenphp/v2Contoh menambahkan modul Caddy komunitas:
./xcaddy build \
--with github.com/php/frankenphp/v2 \
--with github.com/mholt/caddy-l4 \
--with github.com/hairyhenderson/caddy-teapot-moduleModul-modul ini tersedia sebagai directiv e Caddyfile baru setelah build. Untuk menambah modul ke image Docker resmi, jalankan xcaddy di dalam image itu:
FROM dunglas/frankenphp:1.12.7-php8.5-bookworm AS builder
RUN apk add --no-cache go \
&& go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest \
&& xcaddy build --with github.com/php/frankenphp/v2 --with <modul>
FROM dunglas/frankenphp:1.12.7-php8.5-bookworm
COPY --from=builder /usr/bin/frankenphp /usr/local/bin/frankenphp
USER frankenphpUntuk ekstensi yang tidak didukung build skrip (misal ekstensi proprietary), kompilasi sebagai ekstensi dinamis lalu muat:
# dalam image builder yang punya phpize
phpize
./configure --with-foo=/usr/local/foo
make -j$(nproc)
make install # menaruh .so di extension_dirLalu aktifkan di Caddyfile:
{
frankenphp {
php_ini extension=foo.so
}
}--enable-zts menghasilkan binary yang crash atau tidak bisa jalan mode worker. ZTS adalah wajib.install-php-extensions menghindari ini; build manual tidak.gh attestation verify untuk rilis resmi, dan uji staging untuk build sendiri.Pada episode 21 ini, kalian telah menguasai custom build FrankenPHP.
Inti yang harus dibawa pulang:
--enable-embed --enable-zts --disable-zend-signals.install-php-extensions di image resmi../build-static 8.5.15 --with-....--enable-frankenphp).xcaddy build --with github.com/php/frankenphp/v2.Di episode 22 selanjutnya kita menghubungkan FrankenPHP dengan ekosistem CMS & framework: WordPress, Drupal & API Platform — menjalankan WP di FrankenPHP dengan mode worker, Drupal, dan alur kerja API Platform yang sudah matang. Sampai jumpa di episode 22!