From 9b2fb0c5098a604a470b06df79a35740b7c5ad75 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 19:21:19 +0000 Subject: [PATCH 1/5] Strip build-only tooling from the finished image Split apk installs into a runtime set (CLI tools + libs kept explicit) and a virtual .build-deps group holding compilers, headers and -dev packages needed only to compile the PHP extensions. After all extensions are built, scan the compiled .so files with scanelf to detect the shared libraries they actually link against, install those explicitly, then delete .build-deps. Also run docker-php-source delete to drop the extracted PHP source tree, and dedupe a couple of packages that were listed twice. This follows the same pattern the official PHP Docker images use to keep gcc/make/*-dev packages and the PHP source tree out of the final image. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_013HSR8gEQwdQK7LfdNsDRfk --- php-install.sh | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/php-install.sh b/php-install.sh index b8821f5..4549885 100644 --- a/php-install.sh +++ b/php-install.sh @@ -1,7 +1,17 @@ set -eu -apk add --no-cache unixodbc-dev brotli-dev gmp-dev yaml-dev samba-dev libldap openldap-dev pcre-dev libxslt-dev imap-dev sudo git libpng libjpeg libpq libxml2 mysql-client openssh-client rsync patch bash imagemagick libzip-dev \ - imagemagick-libs unixodbc-dev rabbitmq-c rabbitmq-c-dev mpdecimal-dev gettext gettext-dev imagemagick-dev librdkafka-dev autoconf g++ make icu-dev libpng-dev libjpeg-turbo-dev postgresql-dev libxml2-dev bzip2-dev icu icu-dev libmemcached-dev linux-headers $PHPIZE_DEPS +# Runtime tools and libraries that are actually used in the finished image +# (CLI tools invoked directly, plus a couple of libs kept explicit rather +# than relying on auto-detection below). +apk add --no-cache sudo git libpng libjpeg libpq libxml2 libldap mysql-client openssh-client rsync patch bash imagemagick imagemagick-libs rabbitmq-c gettext icu + +# Compilers, headers and -dev packages needed only to build the PHP +# extensions below. Everything in this virtual group is removed near the +# end of the script once the extensions are compiled; the shared libraries +# the compiled .so files actually need at runtime are detected and kept +# separately (see the cleanup step after all extensions are built). +apk add --no-cache --virtual .build-deps unixodbc-dev brotli-dev gmp-dev yaml-dev samba-dev openldap-dev pcre-dev libxslt-dev imap-dev libzip-dev \ + rabbitmq-c-dev mpdecimal-dev gettext-dev imagemagick-dev librdkafka-dev autoconf g++ make icu-dev libpng-dev libjpeg-turbo-dev postgresql-dev libxml2-dev bzip2-dev libmemcached-dev linux-headers pax-utils $PHPIZE_DEPS case $PHP_VERSION in 8.6*) @@ -247,7 +257,7 @@ esac # gd has slightly different build arguments on newer PHP. case $PHP_VERSION in 7.4|8.*) - apk add --no-cache oniguruma-dev + apk add --no-cache --virtual .build-deps oniguruma-dev docker-php-ext-configure gd --with-jpeg=/usr ;; *) @@ -289,7 +299,7 @@ docker-php-ext-enable imagick case $PHP_VERSION in 8.4*|8.5*) - apk add --no-cache krb5-dev + apk add --no-cache --virtual .build-deps krb5-dev # If we really need it. php -m | grep -q '^imap$' || yes | pecl install imap docker-php-ext-enable imap @@ -322,6 +332,25 @@ case $PHP_VERSION in ;; esac +# All PHP extensions are built by this point. Figure out which shared +# libraries the compiled extensions actually link against, keep those +# (regardless of which -dev package originally provided their headers), +# and drop the compilers/headers/-dev packages used to build them. +runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions 2>/dev/null \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ +)" +if [ -n "$runDeps" ]; then + apk add --no-cache --virtual .phpexts-rundeps $runDeps +fi +apk del --no-network .build-deps + +# The PHP source tree extracted by docker-php-ext-configure/-install to +# build extensions from source is only needed at build time. +docker-php-source delete + mkdir ~/.ssh/ ssh-keyscan -t rsa git.drupal.org >> ~/.ssh/known_hosts ssh-keyscan -t rsa gitlab.com >> ~/.ssh/known_hosts From 3cec18faab821bcd6a90b0f359f8b9b25d6cf534 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 20:07:12 +0000 Subject: [PATCH 2/5] Fix build-deps virtual package clobbering itself mid-script apk add --virtual NAME replaces an existing virtual package's dependency list rather than merging into it. Reusing .build-deps for the mid-script oniguruma-dev/krb5-dev installs redefined it down to just that one package, immediately purging everything from the original .build-deps set (including zlib-dev, pulled in transitively) before gd's configure step needed it - which is why the PHP 8.6 build failed with "Package 'zlib' not found". Install those two as plain packages instead, same as before this cleanup. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_013HSR8gEQwdQK7LfdNsDRfk --- php-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/php-install.sh b/php-install.sh index 4549885..afcb4ef 100644 --- a/php-install.sh +++ b/php-install.sh @@ -257,7 +257,7 @@ esac # gd has slightly different build arguments on newer PHP. case $PHP_VERSION in 7.4|8.*) - apk add --no-cache --virtual .build-deps oniguruma-dev + apk add --no-cache oniguruma-dev docker-php-ext-configure gd --with-jpeg=/usr ;; *) @@ -299,7 +299,7 @@ docker-php-ext-enable imagick case $PHP_VERSION in 8.4*|8.5*) - apk add --no-cache --virtual .build-deps krb5-dev + apk add --no-cache krb5-dev # If we really need it. php -m | grep -q '^imap$' || yes | pecl install imap docker-php-ext-enable imap From 701a140fe7cb2b7dd4a6881817c1f955e2d1792e Mon Sep 17 00:00:00 2001 From: Eirik Stanghelle Morland Date: Wed, 8 Jul 2026 09:27:09 +0200 Subject: [PATCH 3/5] Update php-install.sh --- php-install.sh | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/php-install.sh b/php-install.sh index afcb4ef..cb2ada5 100644 --- a/php-install.sh +++ b/php-install.sh @@ -1,15 +1,7 @@ set -eu -# Runtime tools and libraries that are actually used in the finished image -# (CLI tools invoked directly, plus a couple of libs kept explicit rather -# than relying on auto-detection below). apk add --no-cache sudo git libpng libjpeg libpq libxml2 libldap mysql-client openssh-client rsync patch bash imagemagick imagemagick-libs rabbitmq-c gettext icu -# Compilers, headers and -dev packages needed only to build the PHP -# extensions below. Everything in this virtual group is removed near the -# end of the script once the extensions are compiled; the shared libraries -# the compiled .so files actually need at runtime are detected and kept -# separately (see the cleanup step after all extensions are built). apk add --no-cache --virtual .build-deps unixodbc-dev brotli-dev gmp-dev yaml-dev samba-dev openldap-dev pcre-dev libxslt-dev imap-dev libzip-dev \ rabbitmq-c-dev mpdecimal-dev gettext-dev imagemagick-dev librdkafka-dev autoconf g++ make icu-dev libpng-dev libjpeg-turbo-dev postgresql-dev libxml2-dev bzip2-dev libmemcached-dev linux-headers pax-utils $PHPIZE_DEPS @@ -332,10 +324,6 @@ case $PHP_VERSION in ;; esac -# All PHP extensions are built by this point. Figure out which shared -# libraries the compiled extensions actually link against, keep those -# (regardless of which -dev package originally provided their headers), -# and drop the compilers/headers/-dev packages used to build them. runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions 2>/dev/null \ | tr ',' '\n' \ @@ -347,8 +335,6 @@ if [ -n "$runDeps" ]; then fi apk del --no-network .build-deps -# The PHP source tree extracted by docker-php-ext-configure/-install to -# build extensions from source is only needed at build time. docker-php-source delete mkdir ~/.ssh/ From 3acfb55a7c7a81c2e70d8352237a406c8cdffcce Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 13:17:18 +0000 Subject: [PATCH 4/5] Explicitly keep sasl, unixodbc and file across the build-deps cleanup scanelf only sees libraries linked into a compiled .so's ELF NEEDED list, not ones loaded by path/config at runtime. LDAP's SASL bind mechanisms (libsasl2) and unixODBC's driver manager (used by sqlsrv/pdo_sqlsrv) are loaded that way, so they weren't guaranteed to survive the .build-deps removal. file is a generally useful CLI tool that happened to only be present as a side effect of $PHPIZE_DEPS. Keep all three explicit alongside the other runtime packages. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_013HSR8gEQwdQK7LfdNsDRfk --- php-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php-install.sh b/php-install.sh index cb2ada5..7cb7e23 100644 --- a/php-install.sh +++ b/php-install.sh @@ -1,6 +1,6 @@ set -eu -apk add --no-cache sudo git libpng libjpeg libpq libxml2 libldap mysql-client openssh-client rsync patch bash imagemagick imagemagick-libs rabbitmq-c gettext icu +apk add --no-cache sudo git libpng libjpeg libpq libxml2 libldap cyrus-sasl unixodbc file mysql-client openssh-client rsync patch bash imagemagick imagemagick-libs rabbitmq-c gettext icu apk add --no-cache --virtual .build-deps unixodbc-dev brotli-dev gmp-dev yaml-dev samba-dev openldap-dev pcre-dev libxslt-dev imap-dev libzip-dev \ rabbitmq-c-dev mpdecimal-dev gettext-dev imagemagick-dev librdkafka-dev autoconf g++ make icu-dev libpng-dev libjpeg-turbo-dev postgresql-dev libxml2-dev bzip2-dev libmemcached-dev linux-headers pax-utils $PHPIZE_DEPS From 062454411aebff977f228e31821acbb46e812504 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Jul 2026 19:44:43 +0000 Subject: [PATCH 5/5] Clean up the two remaining stray build-only packages oniguruma-dev (gd, PHP 7.4/8.x) and krb5-dev (imap, PHP 8.4/8.5) were plain apk add calls, made that way to work around .build-deps getting redefined instead of merged when reused mid-script. They lingered in every final image instead of being cleaned up like the rest. Give each its own uniquely-named virtual group and delete all of them in the same final scanelf-based cleanup step - the scanelf scan runs after gd/imap are fully built regardless of which named group provided their build-time headers, so it still correctly re-pins whatever those extensions actually link against. Since .build-deps-gd/-imap are only created on some PHP versions (7.3 skips gd's, most versions skip imap's), check apk info -e before deleting them - `set -eu` would abort the whole build on `apk del` failing for a nonexistent virtual package. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_013HSR8gEQwdQK7LfdNsDRfk --- php-install.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/php-install.sh b/php-install.sh index 7cb7e23..a056cd3 100644 --- a/php-install.sh +++ b/php-install.sh @@ -249,7 +249,7 @@ esac # gd has slightly different build arguments on newer PHP. case $PHP_VERSION in 7.4|8.*) - apk add --no-cache oniguruma-dev + apk add --no-cache --virtual .build-deps-gd oniguruma-dev docker-php-ext-configure gd --with-jpeg=/usr ;; *) @@ -291,7 +291,7 @@ docker-php-ext-enable imagick case $PHP_VERSION in 8.4*|8.5*) - apk add --no-cache krb5-dev + apk add --no-cache --virtual .build-deps-imap krb5-dev # If we really need it. php -m | grep -q '^imap$' || yes | pecl install imap docker-php-ext-enable imap @@ -333,7 +333,13 @@ runDeps="$( \ if [ -n "$runDeps" ]; then apk add --no-cache --virtual .phpexts-rundeps $runDeps fi -apk del --no-network .build-deps +extraBuildDeps="" +for extra in .build-deps-gd .build-deps-imap; do + if apk info -e "$extra" >/dev/null 2>&1; then + extraBuildDeps="$extraBuildDeps $extra" + fi +done +apk del --no-network .build-deps $extraBuildDeps docker-php-source delete