Key Takeaways
- When using PHP-FPM Docker images, missing extensions like
pdo_mysqlare often due to volume mounts overwriting theconf.dfolder, not image build issues. docker execshows the state of the running container, whereasdocker runuses a fresh image - a critical distinction when debugging PHP extensions.- The reliable fix: copy
.inifiles into the image first, then mount individual.inifiles instead of the wholeconf.ddirectory. - Avoid mounting subfolders inside
conf.d; PHP only scans.inifiles directly inconf.d.
The Symptom
While building a PHP-FPM Docker image for a Symfony application, everything seemed fine:
FROM php:8.4-fpm AS base
RUN apt-get update && apt-get install -y \
libicu-dev \
acl \
procps \
&& docker-php-ext-install pdo pdo_mysql \
&& docker-php-ext-install intl \
&& docker-php-ext-install opcache \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
However, when checking PHP extensions inside a running container with:
docker exec -it app_scaffold-php-1 php -m
the pdo_mysql extension was missing, even though it had been successfully installed in the Dockerfile.
Running a new container from the same image manually:
docker run app_scaffold-php php -m
showed pdo_mysql and intl correctly loaded.
This created an intermittent extension issue - working in some containers but not others.
Diagnosing the Issue
The main clue came from comparing:
docker execin the Compose container → missing extensionsdocker runfrom the image → extensions present
and inspecting /usr/local/etc/php/conf.d inside the containers.
In the working manual container:
/usr/local/etc/php/conf.d/
docker-php-ext-pdo_mysql.ini
docker-php-ext-intl.ini
docker-php-ext-opcache.ini
scaffold-custom.ini
In the Compose container:
/usr/local/etc/php/conf.d/
scaffold-dev-xdebug.ini
scaffold-dev-custom.local.ini
Observation: all default extension .ini files were gone.
Why this happened
The Compose file mounted a host folder:
- ./config/docker/php/:/usr/local/etc/php/conf.d
- Docker replaces the entire
conf.dfolder with the mounted folder. - Default extension
.inifiles likedocker-php-ext-pdo_mysql.iniwere hidden, so PHP could no longer loadpdo_mysqlorintl. - Any attempt to use subfolders inside
conf.dalso didn't work - PHP only scans.inifiles directly insideconf.d, not subfolders.
Things I Tried That Didn’t Work
-
Mounting the entire
conf.ddirectory- Goal: add custom
.inifiles at runtime - Problem: overwrote all default extension
.inifiles → extensions disappeared
- Goal: add custom
-
Mounting a subfolder inside
conf.d- Goal: keep default extensions while adding custom
.inifiles - Problem: PHP does not scan subfolders → custom
.inifiles were ignored
- Goal: keep default extensions while adding custom
-
Mounting after image build without copying
.inifirst- Goal: first-run copy of custom
.inifiles - Problem: empty directories created by volume mount → no
.inifiles, extensions still missing
- Goal: first-run copy of custom
The Fix
The working approach involved two steps:
-
Copy the
.inifiles, on th host, to the correct locations before image build -
Mount individual
.inifiles instead of the whole directory
volumes:
- ./config/docker/php/scaffold-dev-custom.local.ini:/usr/local/etc/php/conf.d/scaffold-dev-custom.ini:ro
- ./config/docker/php/scaffold-dev-xdebug.ini:/usr/local/etc/php/conf.d/scaffold-dev-xdebug.ini:ro
- Avoids overwriting built-in extension
.inifiles - Allows custom settings to override defaults
- Works for first-run containers and ongoing development
Lessons Learned
- Docker volumes replace, not merge - mounting a folder over
conf.dhides default extension configs. - PHP does not scan subdirectories in
conf.d. Always mount individual.inifiles if you need custom configs. - Always check running container vs image:
docker execshows the container state;docker runuses the built image.
This approach ensures:
- PHP extensions are always loaded correctly
- Custom
.inifiles can be mounted safely - First-run scripts and development workflows work reliably
Originally published at https://chrisshennan.com/blog/intermittent-pdomysql-php-extension-in-docker-diagnosis-and-fix