Skip to content

Do not fail when building PHP 8.5 with opcache #1631

Open

Description

@alexisLefebvre

Today you can build an image with this Dockerfile:

FROM php:8.4.15-cli-alpine

RUN docker-php-ext-install opcache

But if you change php:8.4.15-cli-alpine to php:8.5.0-cli-alpine you will have a strange error:

cp: cannot stat 'modules/*': No such file or directory
Full log
 => CACHED [1/2] FROM docker.io/library/php:8.5.0-cli-alpine@sha256:367aebbaf41fb7193da31082d82325b5123afd508ade860c2ad8deca32fd6cb4                                                                               0.0s
 => ERROR [2/2] RUN docker-php-ext-install opcache                                                                                                                                                                 7.9s
------                                                                                                                                                                                                                  
 > [2/2] RUN docker-php-ext-install opcache:                                                                                                                                                                            
2.123 fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/APKINDEX.tar.gz                                                                                                                                     
2.310 fetch https://dl-cdn.alpinelinux.org/alpine/v3.22/community/x86_64/APKINDEX.tar.gz                                                                                                                                
2.656 (1/26) Installing m4 (1.4.19-r4)                                                                                                                                                                                  
2.674 (2/26) Installing libbz2 (1.0.8-r6)                                                                                                                                                                               
2.693 (3/26) Installing perl (5.40.3-r0)
3.043 (4/26) Installing autoconf (2.72-r1)
3.074 (5/26) Installing pkgconf (2.4.3-r0)
3.092 (6/26) Installing dpkg-dev (1.22.15-r0)
3.132 (7/26) Installing libmd (1.1.0-r0)
3.149 (8/26) Installing dpkg (1.22.15-r0)
3.177 (9/26) Installing libmagic (5.46-r2)
3.218 (10/26) Installing file (5.46-r2)
3.235 (11/26) Installing libstdc++ (14.2.0-r6)
3.268 (12/26) Installing libstdc++-dev (14.2.0-r6)
3.431 (13/26) Installing jansson (2.14.1-r0)
3.449 (14/26) Installing binutils (2.44-r3)
3.535 (15/26) Installing libgomp (14.2.0-r6)
3.554 (16/26) Installing libatomic (14.2.0-r6)
3.570 (17/26) Installing gmp (6.3.0-r3)
3.592 (18/26) Installing isl26 (0.26-r1)
3.622 (19/26) Installing mpfr4 (4.2.1_p1-r0)
3.645 (20/26) Installing mpc1 (1.3.1-r1)
3.661 (21/26) Installing gcc (14.2.0-r6)
4.480 (22/26) Installing musl-dev (1.2.5-r10)
4.552 (23/26) Installing g++ (14.2.0-r6)
4.790 (24/26) Installing make (4.4.1-r3)
4.809 (25/26) Installing re2c (4.2-r0)
4.871 (26/26) Installing .phpize-deps (20251121.165625)
4.873 Executing busybox-1.37.0-r19.trigger
4.883 OK: 304 MiB in 67 packages
4.998 Configuring for:
4.998 PHP Version:             8.5
4.998 PHP Api Version:         20250925
4.998 Zend Module Api No:      20250925
4.998 Zend Extension Api No:   420250925
5.976 checking for grep that handles long lines and -e... /bin/grep
6.014 checking for egrep... /bin/grep -E
6.015 checking for a sed that does not truncate output... /bin/sed
6.032 checking build system type... x86_64-pc-linux-musl
6.036 checking host system type... x86_64-pc-linux-musl
6.036 checking target system type... x86_64-pc-linux-musl
6.053 shtool:echo:Warning: unable to determine terminal sequence for bold mode
6.080 shtool:echo:Warning: unable to determine terminal sequence for bold mode
6.088 checking for gawk... no
6.088 checking for nawk... no
6.088 checking for awk... awk
6.088 checking if awk is broken... no
6.089 checking for pkg-config... /usr/bin/pkg-config
6.090 checking pkg-config is at least version 0.9.0... yes
6.091 checking for cc... cc
6.109 checking whether the C compiler works... yes
6.135 checking for C compiler default output file name... a.out
6.136 checking for suffix of executables... 
6.165 checking whether we are cross compiling... no
6.205 checking for suffix of object files... o
6.229 checking whether the compiler supports GNU C... yes
6.255 checking whether cc accepts -g... yes
6.279 checking for cc option to enable C11 features... none needed
6.343 checking how to run the C preprocessor... cc -E
6.394 checking for egrep -e... (cached) /bin/grep -E
6.394 checking for icc... no
6.405 checking for suncc... no
6.417 checking for system library directory... lib
6.417 checking if compiler supports -Wl,-rpath,... yes
6.462 checking for PHP prefix... /usr/local
6.462 checking for PHP includes... -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib
6.462 checking for PHP extension directory... /usr/local/lib/php/extensions/no-debug-non-zts-20250925
6.462 checking for PHP installed headers prefix... /usr/local/include/php
6.462 checking if debugging is enabled... no
6.478 checking if PHP is built with thread safety (ZTS)... no
6.493 
6.493 Configuring extension
6.494 checking whether to enable copying PHP CODE pages into HUGE PAGES... yes
6.494 checking whether to enable JIT... yes
6.494 checking whether to support opcache JIT disassembly through Capstone... no
6.494 checking for mprotect... yes
6.530 checking for shm_create_largepage... no
6.569 checking for sysvipc shared memory support... yes
6.623 checking for mmap() using MAP_ANON shared memory support... yes
6.673 checking for library containing shm_open... none required
6.709 checking for mmap() using shm_open() shared memory support... yes
6.781 
6.781 Configuring libtool
6.781 checking for a sed that does not truncate output... /bin/sed
6.783 checking for ld used by cc... /usr/x86_64-alpine-linux-musl/bin/ld
6.792 checking if the linker (/usr/x86_64-alpine-linux-musl/bin/ld) is GNU ld... yes
6.795 checking for /usr/x86_64-alpine-linux-musl/bin/ld option to reload object files... -r
6.795 checking for BSD-compatible nm... /usr/bin/nm -B
6.796 checking whether ln -s works... yes
6.796 checking how to recognize dependent libraries... pass_all
6.812 checking for stdio.h... yes
6.830 checking for stdlib.h... yes
6.848 checking for string.h... yes
6.867 checking for inttypes.h... yes
6.887 checking for stdint.h... yes
6.908 checking for strings.h... yes
6.929 checking for sys/stat.h... yes
6.951 checking for sys/types.h... yes
6.973 checking for unistd.h... yes
6.997 checking for dlfcn.h... yes
7.021 checking the maximum length of command line arguments... 98304
7.023 checking command to parse /usr/bin/nm -B output from cc object... ok
7.085 checking for objdir... .libs
7.088 checking for ar... ar
7.088 checking for ranlib... ranlib
7.088 checking for strip... strip
7.140 checking if cc supports -fno-rtti -fno-exceptions... no
7.160 checking for cc option to produce PIC... -fPIC
7.161 checking if cc PIC flag -fPIC works... yes
7.180 checking if cc static flag -static works... yes
7.213 checking if cc supports -c -o file.o... yes
7.234 checking whether the cc linker (/usr/x86_64-alpine-linux-musl/bin/ld -m elf_x86_64) supports shared libraries... yes
7.249 checking whether -lc should be explicitly linked in... no
7.276 checking dynamic linker characteristics... GNU/Linux ld.so
7.282 checking how to hardcode library paths into programs... immediate
7.282 checking whether stripping libraries is possible... yes
7.284 checking if libtool supports shared libraries... yes
7.284 checking whether to build shared libraries... yes
7.284 checking whether to build static libraries... no
7.351 
7.351 creating libtool
7.398 appending configuration tag "CXX" to libtool
7.410 
7.410 Generating files
7.428 configure: creating build directories
7.444 configure: creating Makefile
7.447 configure: patching config.h.in
7.449 configure: creating ./config.status
7.480 config.status: creating config.h
7.526 
7.526 Build complete.
7.528 Don't forget to run 'make test'.
7.530 
7.570 Installing shared extensions:     /usr/local/lib/php/extensions/no-debug-non-zts-20250925/
7.587 cp: can't stat 'modules/*': No such file or directory
7.587 make: *** [Makefile:89: install-modules] Error 1
------
Dockerfile:3
--------------------
   1 |     FROM php:8.5.0-cli-alpine
   2 |     
   3 | >>> RUN docker-php-ext-install opcache
   4 |     
--------------------
ERROR: failed to solve: process "/bin/sh -c docker-php-ext-install opcache" did not complete successfully: exit code: 2

I don't know if PHP Docker images try to maintain backward compatibility but I didn't expect this step to fail, especially with a message that doesn't explain the issue.

Proposals:

  • instead of crashing at build step, docker-php-ext-install should ignore opcache and proceed like it was installed
  • bonus: also display a message like opcache is already installed, please remove it from your Dockerfile

Activity

  1. yosifkit commented on Nov 21, 2025

    @yosifkit
    Member

    In PHP 8.5, opcache is not optional and is included by default: https://wiki.php.net/rfc/make_opcache_required.

    The docker-php-ext-* scripts just work off what is available in the ext directory of the PHP source; we'd rather not maintain a special list of modules that are already installed within the fairly simple scripts.

    The help output of each script has a warning about this:

    echo 'Some of the above modules are already compiled into PHP; please check'
    echo 'the output of "php -i" to see which modules are already loaded.'

  2. hakre commented on Nov 21, 2025

    @hakre

    IMHO, this may kind of defeat itself for a docker build as the docker-php-ext-install is a utility build script, a bit like what @yosifkit was commenting towards.

    However, you made me look into this as I then wondered if this is not supported already or how it could potentially look like and I found the usage in that script.

    $ docker run --rm -it --name gh-1631 php:8.5.0-cli-alpine docker-php-ext-install
    usage: /usr/local/bin/docker-php-ext-install [-jN] [--ini-name file.ini] ext-name [ext-name ...]
       ie: /usr/local/bin/docker-php-ext-install gd mysqli
           /usr/local/bin/docker-php-ext-install pdo pdo_mysql
           /usr/local/bin/docker-php-ext-install -j5 gd mbstring mysqli pdo pdo_mysql shmop
    
    if custom ./configure arguments are necessary, see docker-php-ext-configure
    
    Possible values for ext-name:
    bcmath bz2 calendar ctype curl dba dl_test dom enchant exif ffi fileinfo filter ftp gd gettext gmp hash iconv intl json ldap lexbor mbstring mysqli odbc opcache pcntl pdo pdo_dblib pdo_firebird pdo_mysql pdo_odbc pdo_pgsql pdo_sqlite pgsql phar posix random readline reflection session shmop simplexml snmp soap sockets sodium spl standard sysvmsg sysvsem sysvshm tidy tokenizer uri xml xmlreader xmlwriter xsl zend_test zip
    
    Some of the above modules are already compiled into PHP; please check
    the output of "php -i" to see which modules are already loaded.

    We can see the listing of "Possible values for ext-name:" (the list includes "opcache") and then the hint I'd like to highlight as this caught my attention:

    please check the output of "php -i" to see which modules are already loaded.

    While php -i gives good information about the PHP configuration, a better hint maybe php -m (emm for module).

    $ docker run --rm -it --name gh-1631 php:8.4.15-cli-alpine sh -c 'php --help | grep -F -e -m'
      -m               Show compiled in modules

    Exemplary invocation with output sample:

    $ docker run --rm -it --name gh-1631 php:8.5.0-cli-alpine -m
    [PHP Modules]
    Core
    ctype
    curl
    ...
    xmlwriter
    Zend OPcache
    zlib
    
    [Zend Modules]
    Zend OPcache
    

    It needs some translation work for an identical mapping to the usage listing (that show all lowercase single word ext-name operands) and some filtering (bracketed headers, empty line footers) but in general it shows perhaps the configuration of enabled modules you are looking for @alexisLefebvre (already today and also for previous PHP docker image versions).

    I gave it a try and played with it ad-hoc, found out that the reported 8.4.15 version actually already has it, so perhaps you should have verified that even earlier in your builds:

    $ docker run --rm -it --name gh-1631 php:8.4.14-cli-alpine sh -c 'if php -m | grep -qi "$1$"; then printf "%s already installed\n" "$1"; else docker-php-ext-install -- "$1"; fi' -- opcache
    opcache already installed
    $ docker run --rm -it --name gh-1631 php:8.4.15-cli-alpine sh -c 'if php -m | grep -qi "$1$"; then printf "%s already installed\n" "$1"; else docker-php-ext-install -- "$1"; fi' -- opcache
    opcache already installed

    Feel free to do some more exploration your own @alexisLefebvre, this is just what I wanted to share as I stumbled over when looking into this.

    Ah, it might be intentionally that php -i is suggested, so that builders are actually confronting themselves with the actual utility that is being build (PHP here), as it also shows the whole PHP information and so probably is the best general invocation to suggest anyway. @yosifkit may give a better insight here, also for a comparison with php -m.

  3. alexisLefebvre commented on Nov 21, 2025

    @alexisLefebvre
    Author

    The help output of each script has a warning about this:

    php/docker-php-ext-install

    Lines 40 to 41 in 9ab2e4b
    echo 'Some of the above modules are already compiled into PHP; please check'
    echo 'the output of "php -i" to see which modules are already loaded.'

    This assumes that I should have checked the help, which is a valid point but I forgot / didn’t know it existed. Displaying a message would help users without requiring an extra step.

  4. alexisLefebvre commented on Nov 21, 2025

    @alexisLefebvre
    Author

    I gave it a try and played with it ad-hoc, found out that the reported 8.4.15 version actually already has it, so perhaps you should have verified that even earlier in your builds:

    $ docker run --rm -it --name gh-1631 php:8.4.14-cli-alpine sh -c 'if php -m | grep -qi "$1$"; then printf "%s already installed\n" "$1"; else docker-php-ext-install -- "$1"; fi' -- opcache
    opcache already installed
    $ docker run --rm -it --name gh-1631 php:8.4.15-cli-alpine sh -c 'if php -m | grep -qi "$1$"; then printf "%s already installed\n" "$1"; else docker-php-ext-install -- "$1"; fi' -- opcache
    opcache already installed

    Feel free to do some more exploration your own @alexislefebvre, this is just what I wanted to share as I stumbled over when looking into this.

    I use docker-php-ext-install opcache since PHP 8.3, I didn’t expect it to break when updating the base Docker image.

  5. hakre commented on Nov 22, 2025

    @hakre

    I use docker-php-ext-install opcache since PHP 8.3, I didn’t expect it to break when updating the base Docker image.

    Well, if I get Tim's comment here right, then it was not supposed to get broken at all.

    I now have reproduced it in php-src git and filled issue php/php-src#20557 with a fix of the build error.

  6. TimWolla commented on Nov 23, 2025

    @TimWolla
    Contributor

    Well, if I get Tim's comment here

    For PHP 8.4 and earlier it will not break anything. For PHP 8.5 OPcache does not exist as a separate module, which is also specifically called out in the RFC:

    https://wiki.php.net/rfc/make_opcache_required#backward_incompatible_changes

    I didn’t expect it to break when updating the base Docker image.

    Updating to a new version of PHP will come with changes that might require you to adapt your custom Dockerfile. Consult the migration guide for changes in new PHP versions: https://www.php.net/manual/en/migration85.php

  7. alexisLefebvre commented on Nov 24, 2025

    @alexisLefebvre
    Author

    It looks like we have different expectations about how software should communicate to the users.

    I think that software should do their best to inform its users, even when they do mistakes.

    While some answers here at more or less Read The Manual and that it works as expected.

    If we can’t find a middle ground, then that’s okay; let's agree to disagree, close this issue and move on.

  8. mdprotacio commented on Nov 25, 2025

    @mdprotacio

    php --ri opcache also seems to be broken unlike any other built in extensions.

  9. hakre commented on Nov 25, 2025

    @hakre

    php --ri opcache also seems to be broken unlike any other built in extensions.

    no, actually it is not broken yet. but, did you mean this?

    $ php --ri 'zend opcache'
    
    Zend OPcache
    
    Opcode Caching => Disabled
    ...
    opcache.jit_max_polymorphic_calls => 2 => 2
    $ echo $?
    0
    

    HTH @mdprotacio

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions