Ein passendes php docker image entscheidet oft darüber, ob eine PHP-Anwendung reproduzierbar läuft oder auf jedem Rechner anders reagiert. Ich zeige, welche Varianten sich für CLI-Skripte, Apache und PHP-FPM eignen, wie du ein eigenes Image mit Extensions baust und welche Entscheidungen für Entwicklung und Produktion wirklich zählen.
Mit der richtigen PHP-Variante wird Docker deutlich einfacher
- FPM passt zu NGINX oder Apache als Reverse Proxy und ist meine bevorzugte Produktionsvariante.
- Apache eignet sich für kleinere Anwendungen, bei denen ein einzelner Container genügen soll.
- CLI ist ideal für Composer, Tests, Cronjobs und Framework-Befehle.
-
Versionen pinnen statt blind auf
latestzu setzen, damit Builds reproduzierbar bleiben. - Extensions selbst installieren, weil die offiziellen Images bewusst nicht jede mögliche PHP-Erweiterung enthalten.
Was ein PHP-Image in Docker tatsächlich leistet
Ein Docker-Image bündelt PHP, eine Linux-Basis, benötigte Systembibliotheken und die Startkonfiguration. Daraus entsteht ein Container, der sich auf dem Entwicklerrechner, in der CI-Pipeline und auf dem Server gleich verhält. Genau diese reproduzierbare Laufzeit ist der größte Vorteil gegenüber einer lokal installierten PHP-Version.
Das offizielle PHP-Image wird über Docker Hub bereitgestellt und von der Docker-Community gepflegt. Es stammt also nicht direkt aus dem PHP-Projekt, sondern folgt den Regeln für Docker Official Images. Das ist ein wichtiger Unterschied, weil Support, Tagging und Aktualisierungen über die Docker-Image-Struktur laufen.
Die Suchintention hinter dem Begriff ist vor allem informativ und praktisch. Leser wollen meist nicht nur wissen, was ein Image ist, sondern sofort entscheiden, welchen Tag sie verwenden, wie sie die Anwendung starten und warum Befehle wie docker-php-ext-install manchmal notwendig sind.
CLI, Apache oder FPM welche Variante passt
Die Varianten unterscheiden sich nicht nur im Namen. Sie bestimmen, welcher Prozess im Container läuft und wie die Anwendung HTTP-Anfragen erhält. Meine Faustregel ist einfach: Für Webanwendungen mit separatem Webserver nehme ich FPM, für einfache Einzelcontainer Apache und für Werkzeuge ohne HTTP-Verkehr CLI.
| Variante | Geeignet für | Wichtiger Hinweis |
|---|---|---|
php:8.5-cli |
Composer, Tests, Worker, Cronjobs | Kein Webserver enthalten |
php:8.5-apache |
Kleine PHP-Websites und einfache Deployments | Apache läuft bereits im Container |
php:8.5-fpm |
Laravel, Symfony, WordPress und skalierbare Webanwendungen | Zusätzlicher NGINX- oder Apache-Container nötig |
php:8.5-fpm-alpine3.24 |
Schlanke Produktionsimages | Extensions können schwieriger zu kompilieren sein |
Im Jahr 2026 ist PHP 8.5 ein sinnvoller Ausgangspunkt für neue Projekte, sofern Framework und Bibliotheken diese Version unterstützen. Für bestehende Anwendungen zählt allerdings die Kompatibilität mehr als die höchstmögliche Versionsnummer. Ein sauber getestetes PHP 8.4 kann besser sein als ein übereilter Wechsel auf 8.5.
Bei Debian-basierten Tags wie bookworm oder trixie ist die Installation zusätzlicher Pakete meist unkomplizierter. Alpine erzeugt häufig kleinere Images, verwendet aber musl libc statt glibc. Das kann bei nativen Bibliotheken und PHP-Extensions zu zusätzlicher Fehlersuche führen. Für den ersten produktiven Einsatz bevorzuge ich deshalb meist Debian, selbst wenn Alpine auf dem Papier schlanker wirkt.

Ein eigenes Image mit Extensions bauen
Die offiziellen Images bringen wichtige PHP-Funktionen mit, aber nicht automatisch jede Erweiterung. Prüfe zuerst mit php -m, was bereits vorhanden ist. Viele unnötige Installationsschritte entstehen, weil Entwickler eine Extension kompilieren, die im gewählten Image längst enthalten ist.
Ein einfaches Dockerfile für PHP-FPM mit GD kann so aussehen:
FROM php:8.5-fpm-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j"$(nproc)" gd \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /var/www/html
COPY . /var/www/htmldocker-php-ext-configure setzt Konfigurationsoptionen, während docker-php-ext-install eine Core-Extension kompiliert und aktiviert. Die Entwicklungsbibliotheken werden nur während des Builds gebraucht. Durch das Löschen der Paketlisten bleibt die spätere Image-Schicht kleiner.
Für PECL-Erweiterungen wie Redis oder Xdebug gilt eine andere Vorgehensweise. In produktiven Images pinne ich die Version ausdrücklich, damit ein späterer Build nicht plötzlich eine inkompatible Erweiterung erhält:
RUN pecl install redis-6.1.0 \
&& docker-php-ext-enable redisXdebug gehört meiner Ansicht nach nicht in das Produktionsimage. Für lokale Entwicklung kann es nützlich sein, für den Livebetrieb erzeugt es zusätzlichen Overhead und vergrößert die Angriffsfläche. Besser sind getrennte Entwicklungs- und Produktionsziele im Dockerfile.
Eine PHP-Anwendung mit Docker Compose starten
Bei einer typischen Webanwendung laufen PHP-FPM, NGINX und die Datenbank in getrennten Containern. PHP-FPM verarbeitet PHP-Code, NGINX nimmt HTTP-Anfragen entgegen und die Datenbank bleibt als eigener Dienst austauschbar. Diese Trennung wirkt am Anfang umfangreicher, macht Updates und Skalierung aber deutlich kontrollierbarer.
services:
php:
build:
context: .
volumes:
- ./:/var/www/html
nginx:
image: nginx:1.27
ports:
- "8080:80"
volumes:
- ./:/var/www/html:ro
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
database:
image: mariadb:11
environment:
MARIADB_DATABASE: app
MARIADB_USER: app
MARIADB_PASSWORD: change-me
MARIADB_ROOT_PASSWORD: change-root-me
volumes:
- database_data:/var/lib/mysql
volumes:
database_data:In der NGINX-Konfiguration muss der FastCGI-Upstream auf den Servicenamen php zeigen, nicht auf localhost. Innerhalb eines Containers bedeutet localhost immer dieser eine Container. Das ist einer der häufigsten Fehler bei der ersten PHP-Docker-Konfiguration.
Für die Entwicklung ist ein Bind-Mount wie ./:/var/www/html praktisch, weil Änderungen sofort sichtbar werden. Im Produktionsbetrieb kopiere ich den Code dagegen während des Builds ins Image oder verwende ein unveränderliches Artefakt. So vermeidest du, dass der Server versehentlich lokale Dateien oder geheime Konfigurationsreste einbindet.
Die grundlegenden Befehle bleiben überschaubar:
docker compose build
docker compose up -d
docker compose exec php php -v
docker compose exec php composer install
docker compose logs -f phpDer Befehl docker compose exec php php -v ist besonders hilfreich. Er zeigt nicht die PHP-Version deines Hostsystems, sondern die Version, die deine Anwendung tatsächlich verwendet. Damit lassen sich viele scheinbar mysteriöse Unterschiede zwischen lokalem Rechner und CI-Server sofort erklären.
Versionen, Sicherheit und reproduzierbare Builds
Ein Tag wie php:8.5 ist lesbar und erhält normalerweise Sicherheitsupdates innerhalb dieses Zweigs. Für streng reproduzierbare Builds ist ein vollständiger Patch-Tag oder sogar ein Digest besser. Der Kompromiss besteht darin, dass ein vollständig gepinnter Tag nicht automatisch neue Sicherheitskorrekturen übernimmt.
Ich löse das in Projekten meist über einen kontrollierten Update-Prozess. Die Version steht sichtbar im Dockerfile, wird regelmäßig aktualisiert und anschließend durch Tests, Dependency-Checks und einen Image-Scan geprüft. Blind latest zu verwenden, spart zunächst eine Zeile Konfiguration, erschwert aber jede Fehlersuche.
- Verwende nur unterstützte PHP-Versionen und plane Upgrades vor dem Ende des Sicherheitszeitraums.
- Installiere nur die Extensions, die deine Anwendung wirklich benötigt.
- Lege keine Passwörter oder API-Schlüssel in Dockerfiles und Image-Layer.
- Führe den Webprozess möglichst mit einem nicht privilegierten Benutzer aus.
- Trenne Entwicklungswerkzeuge wie Xdebug von der Produktionsumgebung.
- Prüfe Composer-Abhängigkeiten und das fertige Image regelmäßig auf Schwachstellen.
Ein kleines Image ist nicht automatisch ein sicheres Image. Entscheidend sind ein aktueller Basestand, wenige Abhängigkeiten, nachvollziehbare Builds und ein sauberer Umgang mit Geheimnissen. Gerade bei Alpine wird die reduzierte Größe häufig überschätzt, während Wartbarkeit und Kompatibilität zu wenig Beachtung bekommen.
Auch die Architektur des Servers spielt eine Rolle. Die offiziellen PHP-Images unterstützen mehrere Plattformen wie amd64 und arm64, doch einzelne native Extensions oder Drittanbieter-Bibliotheken können trotzdem architekturspezifische Probleme verursachen. Deshalb teste ich Images immer in derselben Architektur, die später im deutschen Hosting- oder Cloud-System verwendet wird.
Die beste Startentscheidung für dein Projekt
Für eine neue Laravel- oder Symfony-Anwendung würde ich mit php:8.5-fpm-bookworm, NGINX und einer separaten Datenbank beginnen. Diese Kombination ist verständlich, gut dokumentierbar und bei Problemen leichter zu debuggen als ein stark optimiertes Spezialimage.
Für einen kleinen internen Dienst kann php:8.5-apache völlig ausreichen. Geht es dagegen nur um Tests, Migrationen oder einen Queue-Worker, ist php:8.5-cli die passendere und schlankere Wahl. Die richtige Entscheidung hängt also weniger vom Image-Namen ab als vom Prozess, der im Container laufen soll.
Mein wichtigster Rat lautet, das Image so klein wie nötig, aber so verständlich wie möglich zu halten. Ein klarer Tag, ein reproduzierbarer Build und eine getrennte Konfiguration für Entwicklung und Produktion bringen im Alltag meist mehr als die letzten eingesparten Megabytes.