Templated Dockerfile + slim runtime image#11
Conversation
Replace php-8.{3,4,5}/Dockerfile with a single root Dockerfile driven by
versions/<X.Y>.env, and trim the runtime image.
Templating:
- Single Dockerfile parameterized via build args (PHP_VERSION, PHP_API,
extension pins).
- Per-version pins live in versions/8.3.env, 8.4.env, 8.5.env.
- Root tests.yaml replaces the per-version copies (uses php -m, no
ABI-specific paths).
- ci.yml builds by sourcing the env file and passing --build-arg per key.
Image-size cleanup (~28% smaller):
- Strip extension .so files in each builder stage (binutils added to the
compile stage); halves swoole/mongodb among others.
- Replace dev packages with their runtime equivalents in the final stage
(brotli-libs, lz4-libs, zstd-libs, yaml). Drop linux-headers (build-only).
- sockets.so is now compiled once in the compile stage and copied into
final, instead of recompiled (which required linux-headers at runtime).
Extension version unification (across 8.3/8.4):
- redis 6.1.0 -> 6.3.0
- imagick 3.7.0 -> 3.8.1 (8.3 only; 8.4/8.5 already at 3.8.1)
- yaml 2.2.4 -> 2.3.0
- xdebug 3.4.1 -> 3.5.1
Image sizes:
- 8.3: 237MB -> 169MB
- 8.4: 246MB -> 178MB
- 8.5: 262MB -> 194MB
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Greptile SummaryThis PR collapses three per-version Dockerfiles into a single parameterized
Confidence Score: 3/5The build will fail as-is for all three PHP versions due to missing ARG re-declarations in builder stages. The strip commands in every builder stage reference ${PHP_API}, but Docker ARG values do not carry across FROM instructions unless explicitly re-declared. Only the compile and final stages declare ARG PHP_API; the 12 intermediate builder stages do not. The variable expands to an empty string in those stages, producing an invalid path that causes strip (and therefore the RUN layer) to fail, blocking every build variant. The root Dockerfile needs ARG PHP_API added after each of the 12 FROM compile AS lines. Important Files Changed
Reviews (1): Last reviewed commit: "Collapse per-version Dockerfiles into on..." | Re-trigger Greptile |
| FROM compile AS redis | ||
| RUN \ | ||
| # Redis Extension | ||
| git clone --depth 1 --branch $PHP_REDIS_VERSION https://github.com/phpredis/phpredis.git && \ | ||
| cd phpredis && \ | ||
| phpize && \ | ||
| ./configure && \ | ||
| make && make install | ||
| make && make install && \ | ||
| strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## Swoole Extension | ||
| FROM compile AS swoole | ||
| RUN \ | ||
| git clone --depth 1 --branch $PHP_SWOOLE_VERSION https://github.com/swoole/swoole-src.git && \ | ||
| cd swoole-src && \ | ||
| phpize && \ | ||
| ./configure --enable-sockets --enable-http2 --enable-openssl --enable-swoole-curl && \ | ||
| make && make install && \ | ||
| strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so && \ | ||
| cd .. | ||
|
|
||
| ## Imagick Extension | ||
| FROM compile AS imagick | ||
| RUN \ | ||
| git clone --depth 1 --branch $PHP_IMAGICK_VERSION https://github.com/imagick/imagick && \ | ||
| cd imagick && \ | ||
| phpize && \ | ||
| ./configure && \ | ||
| make && make install | ||
| make && make install && \ | ||
| strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## YAML Extension | ||
| FROM compile AS yaml | ||
| RUN \ | ||
| git clone --depth 1 --branch $PHP_YAML_VERSION https://github.com/php/pecl-file_formats-yaml && \ | ||
| cd pecl-file_formats-yaml && \ | ||
| phpize && \ | ||
| ./configure && \ | ||
| make && make install | ||
| make && make install && \ | ||
| strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## Maxminddb extension | ||
| FROM compile AS maxmind | ||
| RUN \ | ||
| git clone --depth 1 --branch $PHP_MAXMINDDB_VERSION https://github.com/maxmind/MaxMind-DB-Reader-php.git && \ | ||
| cd MaxMind-DB-Reader-php && \ | ||
| cd ext && \ | ||
| phpize && \ | ||
| ./configure && \ | ||
| make && make install | ||
| make && make install && \ | ||
| strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| # Zstd Compression | ||
| FROM compile AS zstd | ||
| RUN git clone --recursive -n https://github.com/kjdev/php-ext-zstd.git \ | ||
| && cd php-ext-zstd \ | ||
| && git checkout $PHP_ZSTD_VERSION \ | ||
| && phpize \ | ||
| && ./configure --with-libzstd \ | ||
| && make && make install | ||
| && make && make install \ | ||
| && strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## Brotli Extension | ||
| FROM compile AS brotli | ||
| RUN git clone https://github.com/kjdev/php-ext-brotli.git \ | ||
| && cd php-ext-brotli \ | ||
| && git reset --hard $PHP_BROTLI_VERSION \ | ||
| && phpize \ | ||
| && ./configure --with-libbrotli \ | ||
| && make && make install | ||
| && make && make install \ | ||
| && strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## LZ4 Extension | ||
| FROM compile AS lz4 | ||
| RUN git clone --recursive https://github.com/kjdev/php-ext-lz4.git \ | ||
| && cd php-ext-lz4 \ | ||
| && git reset --hard $PHP_LZ4_VERSION \ | ||
| && phpize \ | ||
| && ./configure --with-lz4-includedir=/usr \ | ||
| && make && make install | ||
| && make && make install \ | ||
| && strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## Snappy Extension | ||
| FROM compile AS snappy | ||
| RUN git clone --recursive https://github.com/kjdev/php-ext-snappy.git \ | ||
| && cd php-ext-snappy \ | ||
| && git reset --hard $PHP_SNAPPY_VERSION \ | ||
| && phpize \ | ||
| && ./configure \ | ||
| && make && make install | ||
| && make && make install \ | ||
| && strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## Scrypt Extension | ||
| FROM compile AS scrypt | ||
| RUN git clone --depth 1 https://github.com/DomBlack/php-scrypt.git \ | ||
| && cd php-scrypt \ | ||
| && git reset --hard $PHP_SCRYPT_VERSION \ | ||
| && phpize \ | ||
| && ./configure --enable-scrypt \ | ||
| && make && make install | ||
| && make && make install \ | ||
| && strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## XDebug Extension | ||
| FROM compile AS xdebug | ||
| RUN \ | ||
| git clone --depth 1 --branch $PHP_XDEBUG_VERSION https://github.com/xdebug/xdebug && \ | ||
| cd xdebug && \ | ||
| phpize && \ | ||
| ./configure && \ | ||
| make && make install | ||
| make && make install && \ | ||
| strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so | ||
|
|
||
| ## MongoDB Extension | ||
| FROM compile AS mongodb | ||
| RUN \ | ||
| git clone --depth 1 --recursive --branch $PHP_MONGO_VERSION https://github.com/mongodb/mongo-php-driver.git && \ | ||
| cd mongo-php-driver && \ | ||
| phpize && \ | ||
| ./configure && \ | ||
| make && make install | ||
| make && make install && \ | ||
| strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-${PHP_API}/*.so |
There was a problem hiding this comment.
ARG PHP_API missing in all builder stages
Docker ARG values do not propagate across FROM instructions — each stage must re-declare every ARG it intends to use. The compile stage declares ARG PHP_API (line 5), but none of the 12 downstream stages (redis, swoole, imagick, yaml, maxmind, zstd, brotli, lz4, snappy, scrypt, xdebug, mongodb) do. As a result ${PHP_API} expands to an empty string in every one of them. The strip command becomes strip --strip-unneeded /usr/local/lib/php/extensions/no-debug-non-zts-/*.so, which references a non-existent directory; strip (binutils) exits non-zero, and the entire build fails for every PHP version.
The fix is to add ARG PHP_API immediately after each FROM compile AS <stage> line — the same pattern already correctly applied to the final stage at line 159.
|
@greptile-apps the ARG-doesn't-cross-FROM claim is a false positive here. That rule applies to fresh base images (e.g. Empirical confirmation: ARG MYVAR
FROM alpine AS base
ARG MYVAR
RUN echo "base: [$MYVAR]" > /tmp/out
FROM base AS child
RUN echo "child: [$MYVAR]" >> /tmp/out
RUN cat /tmp/outBuilt with And on the actual PR: locally-built images show |
|
You're right, and I was wrong to flag this. Looking at the Dockerfile structure:
The "ARG doesn't cross FROM" rule strictly applies to Withdrawing the comment — the code is correct as written.
|
Summary
php-8.{3,4,5}/Dockerfileinto a single rootDockerfileparameterized byversions/<X.Y>.env(PHP_VERSION, PHP_API, and per-extension pins)..sofiles in each builder stage and swap dev packages (*-dev) for their runtime equivalents (*-libs) in the final stage.sockets.soonce in the compile stage and copy it into final (instead of recompiling, which draggedlinux-headersinto the runtime image).tests.yamlreplaces the per-version copies (usesphp -m, no ABI-specific paths).Image size impact
Test plan
tests.yamlfor each version🤖 Generated with Claude Code