Repository navigation
Docker Image Missing PHP Extensions
Fixed in 4.0.0 Β· Reported in #207 by @Dr4inder Β· Module: Docker image (LDAP, LMS, RFP Builder, Domains)
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.
In a container from ghcr.io/edmozley/freeitsm:3.13.0 (and every version up to 3.16.0):
var_export(extension_loaded("ldap")); // falseIn 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.
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_mysqlEvery 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.
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.
-
One list.
docker/php-extensions.txtnames every extension the image must have, and what each one is for. -
The Dockerfile installs it.
ldap,zipandintlare compiled in. The-devpackages 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. - 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.
-
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. -
A guard in CI.
tests/docker-php-extensions.phpscans 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. -
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. -
The setup page reports them.
ldap,zipandintlappear as optional extensions, each saying what it is needed for.
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.
| 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 |
-
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, plusldap_start_tls,idn_to_asciiandZipArchive, 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-queryprints "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,zipandintl- the three the image lacked. Two controls check that the scan really findsldapandzip, so a broken search pattern cannot pass everything.
-
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.
- Installation - what the image includes
- LDAP and Active Directory Β· Basic IMAP mailboxes
- Developer tests: System - the guard test
- Bugs resolved
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
-
Architecture
- β³ π§Ή Root folder tidy (URL routing)
- β³ π Database Integrity (FKs & constraints)
- β³ π οΈ Database Verification β Developer Guide
- β³ π§© Service Layer (UI + API share code)
- β³ πͺ€ Code traps (the TRAP: comments)
- β³ π Commenting for the Engineering wiki
- β³ π οΈ Engineering wiki scanner - Developer Guide
- β³ π Service-Layer progress
- π€ Contributing (read before a pull request)
- π§ͺ Developer tests
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- π± Currencies
- π§ The sidebar (optional module rail)
- π§© Preference templates
- π Display order (modules, Watchtower cards) β Developer Guide
- Theming & Dark Mode
- ποΈ Recent β getting back to what you were doing
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
-
MobileβFriendly
- β³ π« Mobile: Tickets
- β³ π» Mobile: Assets
- β³ π Mobile: Calendar
- β³ π Mobile: Knowledge
- β³ π¦ Mobile: Service Status
- β³ πΌ Mobile: Watchtower
- β³ π§© Mobile: Problem Management
- β³ π Mobile: Change Management
- β³ πΏ Mobile: Software
- β³ β Mobile: Tasks
- β³ π Mobile: Forms
- β³ π Mobile: Contracts
- β³ π Mobile: Domains
- β³ π Mobile: People
- β³ π Mobile: Projects
- β³ ποΈ Mobile: Files
- β³ π Mobile: LMS
- β³ πΊοΈ Mobile: CMDB
- β³ πΊοΈ Mobile: Network Mapper
- β³ π§ Mobile: Process Mapper
- β³ βοΈ Mobile: Workflow
- β³ π₯οΈ Mobile: System
- β³ π Mobile: Reporting
- β³ π Mobile: System Wiki
- β³ π Mobile: Self-Service Portal
- β³ π§° Mobile: Techniques & Tricks
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- β³ π‘οΈ CSRF protection (S4) β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- π CardDAV contact sync
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π Proposal: custom fields, inventory import and change evidence (#194)
- β³ π οΈ Custom fields, evidence + inventory - review (#195-#197)
- β³ π οΈ Long forms in a task's Work view - review (#198)
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π REST API: Domains
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ π·οΈ REST API: Cost centres
- β³ π§βπΌ REST API: Analysts
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ π οΈ Inbox.js map β Developer Guide
- β³ π οΈ Tickets settings β Developer Guide
- β³ π Ticket workspace - design (PR #206)
- β³ π Rota copy and paste β Developer Deep Dive
- β³ β Checklists & SOPs
- β³ βοΈ Mandatory fields
- β³ π§Ύ Custom ticket fields
- β³ π·οΈ Ticket categories
- β³ π₯ Assigning tickets to a team, and escalation
- β³ π’ One board across every company
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ π Attachment size limits and large emails
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
-
β³
βοΈ Telegram channel - β³ β CSAT company scope and filters β Developer Guide
- β³ π₯ Microsoft Teams channel
- β³ π¨οΈ Mattermost channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ β Record previews
- β³ π Ticket notes: internal or shared
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ π¨ Telling the analyst a ticket is theirs
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ π Confidential tickets
- β³ π₯ Portal managers
- β³ π Who has seen a ticket
- β³ π Reading long tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π½ Just my tickets, or no closed ones
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Case reports - a ticket and its work as one PDF
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
- π Projects
-
Assets
- β³ π’ Moving an asset between companies
- β³ π Shared asset locations
- β³ π§βπΌ Assigning assets to analysts
- β³ π Warranty and lease alerts
- β³ π Leases
- β³ π Saved table views
- β³ π¨οΈ Recording anything, and importing it
- β³ π§© Asset type layouts
- β³ π Inventory freshness
- β³ π Connected assets
- β³ π Asset review
- β³ π·οΈ QR asset labels
- β³ π Who holds what, and handover documents
- β³ π₯οΈ The inventory agent (PowerShell)
- β³ ποΈ Proxmox VE servers
- β³ βοΈ VMware Cloud Director servers
- β³ π Linking equipment to tickets
- β³ βοΈ Follow-up tasks on a ticket
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
-
Forms
- β³ π¨ The form designer β Developer Guide
- β³ π Layout & the grid β Developer Guide
- β³ ποΈ Collections β grouping submissions
- β³ π Submissions as PDFs
- β³ β‘ What happens next β a form's own actions
- β³ π οΈ Sections & conditional logic β Developer Guide
- β³ π οΈ Lookup fields β Developer Guide
- β³ π‘οΈ Catalogue request approvals
- People
- Domains
- ποΈ Files
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- π‘ Zabbix (monitoring)
- π¬ Microsoft Teams alerts
- π 3CX (phone system)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ π’ One board across every company
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)
- What this is
-
π Bugs resolved
- β³ π³ The Docker image was missing PHP extensions
- β³ π A failed DNS lookup read as a missing record
- β³ π A link could change data
- β³ π Access outlived its removal
- β³ π Knowledge articles could run scripts
- β³ π Knowledge writes ignored permissions
- β³ π Company boundaries (Oct 2026)
- β³ π Smaller gaps (Oct 2026)
- β³ π Escaped text could leave its attribute
- β³ π Task descriptions were not cleaned
- β³ π Task documents ignored company scope
- β³ π Repeating tasks ignored company scope
- β³ π¬ #183 Mailbox checks overlapped
- β³ π #188 Transfer lock shown off when unknown
- β³ β #184 Subtask names and Overdue
- β³ π§© #186 Teams: the buttons did nothing
- β³ π Text in a change could run script
- β³ π AI settings changed without the AI permission
- β³ πͺ #178 Deleting an analyst failed
- β³ π« A team with no departments showed 0 tickets
- β³ π Projects: adding a phase did nothing on Save
- β³ π Leavers could still sign in
- β³ πΌοΈ Logo and courses broke on Apache with PHP-FPM
- β³ π’ Chat tickets ignored your ticket numbering
- β³ π Dates shown as a dash, or in server time
- β³ π Assets β Users showed people from other companies
- β³ π Restricted analysts could read other modules' data
- β³ πΌοΈ Replies with a picture in the thread failed to send
- β³ π Reply attachments never reached the customer
- β³ π οΈ Outbound email attachments β Developer Guide
- β³ π A global SSO provider was missing from the portal
- β³ π Behind a proxy, the SSO redirect said http
- β³ βοΈ The portal tagline moved when you saved it
- β³ π¨ The portal settings screen forgot what you saved
- β³ π‘οΈ The approvals inbox said "Error" and nothing else
- β³ π A table's answers were missing from the PDF
- β³ β A single-select column let you tick every option
- β³ π The portal ignored a form's field widths
- β³ π The tasks board stopped taking clicks
- β³ ποΈ #121 The index list is out of date after upgrading
- β³ π #133 The calendar subscription was empty
- β³ π #131 Tasks always reopened on the board
- β³ π₯ #129 Every page returned HTTP 500 after upgrading
- β³ π³ #127 A PHP warning above the System page
- β³ π #126 Notes stamped with the server's clock
- β³ π Storing every date in UTC
- β³ πͺ The portal was down for everyone signed in
- β³ βοΈ #120 Workflow notes could never be written
- β³ βοΈ #123 Three errors when running Database Verification
- β³ π #122 The description box was a stub in the corner
- β³ π£ Demo data deleted real accounts
- β³ π #117 Sign-in redirected to the wrong address
- β³ π¨ #108 The priority dot was invisible
- β³ β±οΈ #116 Time logged from the right-click menu
- β³ π #114 API keys refused by our own guard
- β³ ποΈ #110 Assigning a task told nobody
- β³ πͺ #107 Signed out while still working
- β³ π #103 "Share with Requester" reached nobody
- β³ π #102 Search found nothing for hyphens
- β³ πͺ #101 Source code editor opened behind
- β³ βοΈ #88 Subtasks could not be ticked off
- β³ π» #84 Asset deep link selected nothing
- β³ π« #79 A new ticket arrived with no status
- β³ π§ #79 A ticket from email did not say so
- β³ π #78 Bell opened to nothing
- β³ π¬ #77 Mail only collected from Inbox
- β³ π #74 The default password could not be changed
- β³ π¦ #70 Renaming an impact level
- β³ π€ #67 App-only mailboxes could not send
- β³ π #45 Verify only ever worked for Microsoft
- β³ π #45 IMAP reported as not authenticated
- β³ βοΈ An email template stopped escaping itself
- β³ π The portal dashboard showed the wrong time
- β³ π’ The folder said 99 and the list showed 96