Skip to content

Docker Image Missing PHP Extensions

Ed Mozley edited this page Oct 25, 2026 · 2 revisions

The Docker image was missing PHP extensions

Fixed in 4.0.0 Β· Reported in #207 by @Dr4inder Β· Module: Docker image (LDAP, LMS, RFP Builder, Domains)


The short version

The official Docker image installed only one PHP extension beyond the ones PHP builds in: pdo_mysql, for the database. So LDAP / Active Directory sign-in could not work in the container. That was the report. Two more were missing as well. Without zip, uploading an LMS course and reading Word files in the RFP Builder failed. Without intl, Domains refused international domain names such as mΓΌnchen.de.

From 4.0.0 the image includes all three. The build also checks they load and refuses to finish if one does not, so an image without them cannot be published again. Basic IMAP mailboxes are still not supported in the container, for a reason explained below.

What you saw

In a container from ghcr.io/edmozley/freeitsm:3.13.0 (and every version up to 3.16.0):

var_export(extension_loaded("ldap"));   // false

In the app, System β†’ Single Sign-On warned that the LDAP extension was missing, and an LDAP or Active Directory provider could not be used. The reporter had to build their own image on top of ours to add it.

Nobody else had reported the other two, because the features are used less often. Uploading a SCORM course in LMS gave a broken response rather than an error message. Uploading a .docx to the RFP Builder stopped at the text extraction.

What was wrong

The image starts from the official php:8.4-apache image. That has the extensions PHP builds in by default - curl, mbstring, openssl, sodium, dom and the rest. Anything beyond those has to be compiled in by the Dockerfile, and ours only did one:

RUN docker-php-ext-install pdo pdo_mysql

Every feature that needed another extension was written and tested on a development machine whose PHP already had it. The Docker image was only ever built when a release was published, and nothing asked it which extensions it had.

It was three extensions, not one

A sweep of the code for every extension the base image does not include found:

Extension Used by In the container, before 4.0.0
ldap LDAP / Active Directory sign-in, directory sync Could not be used. The SSO page said why.
zip LMS course upload (SCORM), RFP Builder .docx text, search inside .docx and .xlsx attachments Course upload and .docx extraction crashed: new ZipArchive() without the extension is a PHP Error, which the code's catch (Exception) does not catch. Attachment search already checked first and skipped them.
intl International domain names (mΓΌnchen.de β†’ xn--mnchen-3ya.de) in Domains Handled, but refused: the code checks first and says the server cannot handle international domain names. Plain ASCII names were unaffected.
imap Basic IMAP mailboxes Missing - and still missing on purpose, see below.

Spreadsheet import and export (Cost Centres) did not have the problem. When it was written, the missing zip extension in the image was spotted, and it reads and writes .xlsx without it. The same knowledge was not applied anywhere else.

The fix

  1. One list. docker/php-extensions.txt names every extension the image must have, and what each one is for.
  2. The Dockerfile installs it. ldap, zip and intl are compiled in. The -dev packages needed to compile them are removed afterwards, and only the runtime libraries the extensions link against are kept, the same way the official PHP images do it.
  3. The build checks itself. Straight after installing, the Dockerfile reads the list and stops the build if any extension does not load, or if LDAP has no StartTLS. The release workflow builds for amd64 and arm64, so both are checked, and a failed check means nothing is published.
  4. A build on every change. A new workflow, Docker build check, builds the image without publishing it whenever the Dockerfile or docker/ changes. It asks the finished image for each extension, then starts the container and loads the setup page. Before this, a broken Dockerfile would only have been found while publishing a release.
  5. A guard in CI. tests/docker-php-extensions.php scans the code for every extension it uses and fails if one is neither in the list nor excluded with a reason. The next feature that needs a new extension cannot slip past.
  6. No more crashes without zip. On any server without zip, LMS course upload and the RFP Builder now say plainly that the extension is needed.
  7. The setup page reports them. ldap, zip and intl appear as optional extensions, each saying what it is needed for.

The one still missing: imap

PHP 8.4 removed the imap extension from PHP itself and moved it to PECL, a separate add-on. Building it needs a C library called c-client, and current Debian - which the official PHP image is based on - no longer packages it. So Basic IMAP mailboxes do not work in the container. Microsoft 365 and Gmail mailboxes do not need the extension and are not affected.

This is written into the list under NOT IN THE IMAGE, so the guard test knows it is deliberate. The lasting fix is an IMAP client that needs no extension at all, the way SMTP sending already works (#50). See Basic IMAP mailboxes.

πŸ“ Files

File Change
🟒 docker/php-extensions.txt New - the list, what each extension is for, and why imap is excluded
🟑 Dockerfile Installs pdo_mysql, ldap, zip, intl; keeps only runtime libraries; smoke check
🟒 .github/workflows/docker-build-check.yml New - builds the image unpublished on every Docker change, checks the extensions, starts it
🟒 tests/docker-php-extensions.php New - every extension the code uses must be installed or excluded with a reason
🟑 .github/workflows/web-exposure.yml Runs the new test
🟑 api/lms/courses.php, includes/rfp_docx_parser.php A plain message instead of a crash without zip
🟑 setup/index.php, lang/en/setup.php ldap, zip, intl reported as optional extensions

How it was proved

  • The image itself. The Docker build check built the image on GitHub. The Dockerfile's own check printed PHP extensions OK: pdo_mysql, ldap, zip, intl. Asked separately, the finished image reported all four loaded, plus ldap_start_tls, idn_to_ascii and ZipArchive, on PHP 8.4.27. The container started and served the setup page with HTTP 200.
  • The check catches a failure. The first build of the new Dockerfile failed. Current Debian's dpkg-query prints "diversion by …" lines that the clean-up step had not expected, so it tried to keep packages that do not exist. The build stopped and nothing was published, which is exactly what the check is for. The fix was the filter the official PHP images use.
  • The guard catches a missing extension. Its first run, before the list reader was right, failed on ldap, zip and intl - the three the image lacked. Two controls check that the scan really finds ldap and zip, so a broken search pattern cannot pass everything.

What this means for you

  • If you run FreeITSM in Docker and built your own image to add ldap: from 4.0.0 you can go back to the official image.
  • If you use LDAP or Active Directory in Docker: it works from 4.0.0 with no extra steps. Set it up as in LDAP and Active Directory.
  • If you use Basic IMAP mailboxes in Docker: they still do not work there. Use Microsoft 365 or Gmail, or run FreeITSM outside Docker until #50 is done.
  • If you run FreeITSM without Docker: nothing changes. The setup page now tells you which of these extensions you have.

Related pages

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally