Docker Compose: PHP-Umgebung mit einem Klick – DNMP-Tutorial (Nginx+MySQL+PHP)

Im Terminal rollen Fehlermeldungen – „Nginx findet die PHP-FPM-Socket-Datei nicht“. Der achte Fehler heute Abend. LNMP manuell installiert, nginx.conf, php-fpm.conf und my.cnf angepasst, Services ein Dutzend Mal neu gestartet – die Umgebung läuft trotzdem nicht.
Der Kollege am Nachbartisch sagt: „Bei mir läuft das doch – hast du vielleicht die falsche PHP-Version?“
Er nutzt PHP 7.4, ich habe 8.1 installiert. Er MySQL 5.7, ich aus Versehen 8.0. Kein Wunder, dass der Code nicht durchläuft.
Jeder im Team hatte eine andere Entwicklungsumgebung. Neueinsteiger brauchten einen halben Tag Setup, erfahrene Entwickler mussten 1:1 helfen. Code lief auf Rechner A, auf Rechner B nicht. Deployment auf den Server wurde zur Lotterie.
Mit Docker Compose und DNMP sind diese Probleme weg. In 10 Minuten stehen Nginx, MySQL und PHP – alle nutzen dieselbe docker-compose.yml. Niemand sagt mehr „bei mir läuft es“.
Warum Docker Compose für PHP-Umgebungen
Drei Schmerzpunkte der klassischen LNMP-Installation
Schmerzpunkt 1: Manuelle Installation – ein Fehler, alles hängt
Erinnern Sie sich an die erste LNMP-Installation? Zuerst apt-get install nginx, dann php-fpm, dann MySQL. Danach Konfiguration: nginx.conf mit Location-Regeln, php.ini mit Extensions, my.cnf mit Parametern.
Drei Config-Dateien in verschiedenen Verzeichnissen: /etc/nginx/, /etc/php/, /etc/mysql/. Ein Tippfehler – der Service startet nicht. Logs von Nginx, PHP-FPM und MySQL – bis man schwindelig wird.
Ich habe gesehen, wie jemand in php-fpm.conf listen = 127.0.0.1:9000 in listen = /run/php/php7.4-fpm.sock geändert hat. Nginx fand PHP nicht – drei Stunden Debugging.
Schmerzpunkt 2: Versionskompatibilität
Im Team gibt es immer „Versions-Fossilien“. Altes Projekt auf PHP 5.6, Neues auf PHP 8.1. GROUP BY in MySQL 5.7, in 8.0 Fehler wegen ONLY_FULL_GROUP_BY.
Schlimmer: unterschiedliche Versionen pro Rechner:
- Mac mit PHP 7.4 + MySQL 5.7
- Ubuntu mit PHP 8.0 + MySQL 8.0
- Windows mit PHP 8.1 + MariaDB 10.6
Code läuft bei Person A, nach git pull bei Person B Fehler. Test und Produktion wieder andere Versionen – ein Rezept für Nachtschichten.
Schmerzpunkt 3: Team-Effizienz
Tag eins für Neueinsteiger: halber Tag Setup mit Anleitung:
- Nginx installieren (20 Min.)
- PHP inkl. Extensions (30 Min., oft kompilieren)
- MySQL (15 Min.)
- Services verbinden (1 Stunde, viele Stolpersteine)
- Testdaten importieren (10 Min.)
Mit Glück nachmittags coden – ohne Glück erst morgen.
In einem Startup scheiterte eine einfache Registrierung: lokal OK, live „Datenbankverbindung fehlgeschlagen“. Dev-MySQL ohne root-Passwort, Produktion mit Passwort – Config nicht angepasst. Nacht-Rollback.
Drei Vorteile von Docker Compose
Vorteil 1: Configuration as Code – eine Datei fürs Team
Alles in docker-compose.yml:
- Welche Nginx-Version? (
nginx:1.25-alpine) - Welche PHP-Extensions? (
mysqli,pdo_mysql,redis) - MySQL-Parameter? (einheitliche
my.cnf) - Service-Kommunikation? (automatische DNS-Auflösung)
Datei ins Git – alle haben dieselbe Umgebung. Onboarding: Projekt klonen, docker-compose up -d, Kaffee – fertig.
Keine Fragen mehr: „Wie lautet dein MySQL-Passwort?“, „Wo liegt deine php.ini?“
Vorteil 2: Ein-Klick-Start und sauberes Entfernen
Alle Services starten:
docker-compose up -d
Stoppen und Container entfernen:
docker-compose down
PHP-Version wechseln? Eine Zeile: image: php:7.4-fpm → image: php:8.1-fpm, neu up.
Kein mühsames Deinstallieren mit Restdateien. Container sind isoliert – weg ist weg.
Vorteil 3: Isolation und mehrere Versionen parallel
PHP 7.4 und 8.1 gleichzeitig? Klassisch: zwei PHP-Installationen, Ports, zwei fastcgi_pass in Nginx – aufwendig.
Mit Docker Compose zwei PHP-Services:
php74:
image: php:7.4-fpm
php81:
image: php:8.1-fpm
Nginx zeigt pro Projekt auf php74 oder php81 – ohne Konflikt.
Das DNMP-Projekt imeepo/dnmp hat 5000+ Stars – bewährte Lösung für viele Teams.
DNMP-Architektur im Detail
Was ist DNMP
DNMP ist LNMP in Docker:
- D = Docker: Container-Plattform, jeder Service in einem „Container“
- N = Nginx: Webserver, HTTP-Anfragen, Weiterleitung an PHP
- M = MySQL: relationale DB (alternativ MariaDB, PostgreSQL)
- P = PHP: PHP-FPM zur Ausführung von PHP-Code
Oft zusätzlich:
- Redis: Cache
- PHPMyAdmin: grafische MySQL-Verwaltung
Klassisch alles auf dem Host gemischt. DNMP trennt Services in Container – leichter als VMs.
Service-Orchestrierung
Wie ein Hi-Fi-System: Verstärker, Lautsprecher, Player – verbunden über Kabel.
Nginx, PHP und MySQL sind getrennte Container, verbunden über ein Docker-Netzwerk:
Request-Flow:
- Browser →
localhost:80 - Nginx erkennt PHP-Datei
- Weiterleitung an PHP über Port 9000 (Service-Name
php) - PHP verbindet bei Bedarf mit MySQL (Service-Name
mysql) - Antwort zurück an Nginx
- HTML an den Browser
Kommunikation:
Docker Compose legt ein Bridge-Netzwerk an. Service-Namen werden per DNS aufgelöst.
PHP verbindet sich mit mysql:3306, nicht 127.0.0.1:3306.
Port-Mapping:
- Nginx: Container 80 → Host 80
- MySQL: 3306 → 3306 (Navicat & Co.)
- PHPMyAdmin: 80 → Host 8080
- Redis: 6379 → 6379
Volumes:
Wichtige Pfade auf den Host gemappt:
./www→/var/www/html(Code)./mysql/data→/var/lib/mysql(Daten)./nginx/logs→/var/log/nginx(Logs)
Container neu erstellen – Daten bleiben auf dem Host.
Verzeichnisstruktur
Typisches DNMP-Layout:
dnmp/
├── docker-compose.yml # Orchestrierung aller Services
├── .env # Umgebungsvariablen (Passwörter, Ports)
├── .gitignore # .env nicht committen
├── nginx/
│ ├── conf.d/
│ │ └── default.conf # VHost, PHP-Weiterleitung
│ └── logs/
│ ├── access.log
│ └── error.log
├── php/
│ ├── Dockerfile # Extensions
│ ├── php.ini
│ └── php-fpm.conf
├── mysql/
│ ├── data/
│ └── my.cnf
└── www/
└── index.php
Wichtige Dateien:
docker-compose.yml: Images, Mounts, Ports.env: z. B.MYSQL_ROOT_PASSWORD=...nginx/conf.d/default.conf: Document Root,fastcgi_passphp/Dockerfile: Extensions wie mysqli, redismysql/data/: persistente DB-Dateien
Klare Struktur statt verstreuter LNMP-Configs.
Praxis: DNMP in 10 Minuten
Voraussetzungen
1. Docker installieren
-
Mac/Windows: Docker Desktop – inkl. Docker Compose
-
Linux (Ubuntu):
sudo apt update sudo apt install docker.io docker-compose -y sudo systemctl start docker sudo systemctl enable docker
2. Installation prüfen
docker --version
# Ausgabe: Docker version 24.0.6, build xxx
docker-compose --version
# Ausgabe: Docker Compose version v2.21.0
3. Empfohlene Ressourcen
- RAM: mindestens 4 GB (in Docker Desktop einstellbar)
- Disk: ca. 20 GB frei
- Netzwerk: Zugriff auf Docker Hub (ggf. Mirror)
Variante 1: Bewährtes Open-Source-Projekt (empfohlen)
1. Repository klonen
imeepo/dnmp – auch Arm/Apple Silicon:
git clone https://github.com/imeepo/dnmp.git
cd dnmp
2. Umgebungsvariablen
cp .env.example .env
In .env anpassen:
# MySQL root-Passwort (nicht 123456)
MYSQL_ROOT_PASSWORD=your_strong_password
TZ=Asia/Shanghai
# Ports (bei belegtem 80: 8080)
NGINX_HTTP_PORT=80
MYSQL_PORT=3306
3. Start
docker-compose up -d
Beim ersten Mal Images pullen – einige Minuten. Erfolg:
Creating network "dnmp_default" with the default driver
Creating dnmp_mysql_1 ... done
Creating dnmp_php_1 ... done
Creating dnmp_nginx_1 ... done
Creating dnmp_redis_1 ... done
4. Prüfen
http://localhost → phpinfo mit Version und Extensions.
http://localhost:8080 → PHPMyAdmin:
- Server:
mysql(nicht localhost) - User:
root - Passwort: aus
.env
5. Container-Status
docker-compose ps
Beispiel:
Name Command State Ports
--------------------------------------------------------------------
dnmp_nginx_1 nginx -g daemon off; Up 0.0.0.0:80->80/tcp
dnmp_php_1 php-fpm Up 9000/tcp
dnmp_mysql_1 docker-entrypoint... Up 0.0.0.0:3306->3306/tcp
dnmp_redis_1 redis-server Up 6379/tcp
State: Up = läuft.
Variante 2: Eigene docker-compose.yml (fortgeschritten)
1. Projekt anlegen
mkdir my-dnmp && cd my-dnmp
mkdir -p nginx/conf.d php mysql/data www
2. docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:1.25-alpine
container_name: dnmp-nginx
ports:
- "80:80"
volumes:
- ./www:/var/www/html
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/logs:/var/log/nginx
depends_on:
- php
networks:
- dnmp-network
php:
build: ./php
container_name: dnmp-php
volumes:
- ./www:/var/www/html
networks:
- dnmp-network
mysql:
image: mysql:8.0
container_name: dnmp-mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root123456
MYSQL_DATABASE: test_db
TZ: Asia/Shanghai
volumes:
- ./mysql/data:/var/lib/mysql
networks:
- dnmp-network
redis:
image: redis:7-alpine
container_name: dnmp-redis
ports:
- "6379:6379"
networks:
- dnmp-network
networks:
dnmp-network:
driver: bridge
3. PHP Dockerfile
php/Dockerfile:
FROM php:8.1-fpm
RUN apt-get update && apt-get install -y \
libzip-dev \
zip \
unzip
RUN docker-php-ext-install \
mysqli \
pdo_mysql \
zip \
opcache
RUN pecl install redis && docker-php-ext-enable redis
WORKDIR /var/www/html
4. Nginx VHost
nginx/conf.d/default.conf:
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php index.html;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ \.(js|css|png|jpg|gif|ico)$ {
expires 7d;
}
}
5. Testdateien
www/index.php:
<?php
phpinfo();
www/db_test.php:
<?php
$host = 'mysql';
$user = 'root';
$pass = 'root123456';
$db = 'test_db';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
echo "Datenbankverbindung erfolgreich!<br />";
echo "MySQL-Version: " . $pdo->getAttribute(PDO::ATTR_SERVER_VERSION);
} catch(PDOException $e) {
echo "Verbindung fehlgeschlagen: " . $e->getMessage();
}
6. Start mit Build
docker-compose up --build -d
Verifikation
1. PHP
http://localhost → phpinfo, PHP 8.1, Extensions enabled.
2. Datenbank
http://localhost/db_test.php → Erfolgsmeldung und Versionsnummer.
Bei Fehler:
docker-compose ps– MySQLUp?hostmussmysqlsein, nichtlocalhost- Passwort mit
.envabgleichen
3. Logs
docker-compose logs php
docker-compose logs -f nginx
Best Practices für Teams
Versionskontrolle
Prinzip: Config committen, Secrets nicht
.gitignore:
.env
mysql/data/
nginx/logs/*.log
php/logs/*.log
www/*
!www/.gitkeep
Committen:
git add docker-compose.yml
git add .env.example
git add nginx/conf.d/
git add php/Dockerfile
git add php/php.ini
git add mysql/my.cnf
git commit -m "feat: add DNMP environment config"
git push
Onboarding (ca. 3 Minuten):
git clone ...cp .env.example .env- Passwort in
.envsetzen docker-compose up -ddocker exec -i dnmp-mysql mysql -uroot -p < backup.sql
Mehrere PHP-Versionen
services:
php74:
image: php:7.4-fpm
container_name: dnmp-php74
volumes:
- ./www:/var/www/html
networks:
- dnmp-network
php81:
image: php:8.1-fpm
container_name: dnmp-php81
volumes:
- ./www:/var/www/html
networks:
- dnmp-network
nginx/conf.d/old-project.conf:
server {
listen 80;
server_name old.local;
root /var/www/html/old-project;
location ~ \.php$ {
fastcgi_pass php74:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
nginx/conf.d/new-project.conf:
server {
listen 80;
server_name new.local;
root /var/www/html/new-project;
location ~ \.php$ {
fastcgi_pass php81:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
/etc/hosts:
127.0.0.1 old.local
127.0.0.1 new.local
Backup
scripts/backup.sh:
#!/bin/bash
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
MYSQL_CONTAINER="dnmp-mysql"
MYSQL_USER="root"
MYSQL_PASSWORD="root123456"
DATABASE="test_db"
mkdir -p $BACKUP_DIR
echo "Backup von $DATABASE..."
docker exec $MYSQL_CONTAINER mysqldump -u$MYSQL_USER -p$MYSQL_PASSWORD $DATABASE > $BACKUP_DIR/${DATABASE}_${DATE}.sql
echo "Fertig: $BACKUP_DIR/${DATABASE}_${DATE}.sql"
Cron (täglich 2 Uhr):
crontab -e
0 2 * * * /path/to/scripts/backup.sh
Restore:
docker exec -i dnmp-mysql mysql -uroot -proot123456 test_db < ./backups/test_db_20251218.sql
Häufige Probleme
Problem 1: Port belegt
Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use
Prüfen:
lsof -i :80
# Windows: netstat -ano | findstr :80
Lösung: Apache/IIS stoppen oder NGINX_HTTP_PORT=8080 in .env.
Problem 2: Dateirechte (Linux/Mac)
403 Forbiddenin NginxPermission deniedbeim Schreiben
Ursache: UID von www-data (33) ≠ Host-User (z. B. 1000).
Dev-Workaround:
chmod -R 777 ./www
Sauberer: UID in php/Dockerfile anpassen:
FROM php:8.1-fpm
RUN usermod -u 1000 www-data && groupmod -g 1000 www-data
docker-compose build php
docker-compose up -d
Problem 3: PHP-Extension fehlt
Fatal error: Call to undefined function mysqli_connect()
docker exec dnmp-php php -m
In php/Dockerfile:
RUN docker-php-ext-install mysqli pdo_mysql
docker-compose build php
docker-compose restart php
Problem 4: DB-Verbindung
SQLSTATE[HY000] [2002] Connection refused
Typische Fehler:
host=localhostoder127.0.0.1(falsch im Container)- MySQL noch nicht bereit
- falsches Passwort
Richtig:
$host = 'mysql';
$user = 'root';
$pass = 'root123456';
try {
$pdo = new PDO("mysql:host=$host;dbname=test_db", $user, $pass);
echo "Verbindung erfolgreich";
} catch(PDOException $e) {
echo "Fehler: " . $e->getMessage();
}
docker-compose logs mysql
Warten auf mysqld: ready for connections.
Problem 5: Container exitiert sofort
docker-compose ps
# Status Exit 1 oder 127
docker-compose logs <service>
Ursachen:
- Config-Syntax (fehlendes
;in Nginx, YAML-Einrückung mit Spaces) - fehlende
MYSQL_ROOT_PASSWORD - abhängiger Service startet nicht
docker-compose down
docker-compose up -d
Fazit
Drei Kernpunkte für DNMP mit Docker Compose:
- Zeit sparen: ca. 10 Minuten statt halber Tag Setup
- Weniger Bugs: einheitliche Umgebung, kein „bei mir läuft es“ um 2 Uhr nachts
- Wartbar: Config ist Dokumentation – Neueinsteiger verstehen es ohne Handbuch
Früher LNMP manuell – oft bis 2 Uhr morgens. Mit Docker Compose: up, Kaffee, fertig.
Vor Feierabend 10 Minuten testen: git clone https://github.com/imeepo/dnmp.git. Später eigene docker-compose.yml schreiben.
Bei Problemen:
docker-compose logs- GitHub Issues durchsuchen
- Issue im Projekt-Repo öffnen
Docker Compose funktioniert nicht nur für PHP – Node.js, Python, Go nutzen dasselbe Muster.
Nächste Schritte:
- Jetzt:
git clone https://github.com/imeepo/dnmp.git - Vertiefen: Docker-Compose-Doku (z. B.
docker-compose.override.yml) - Teilen: Kollegen mit manueller LNMP-Installation
PHP-Umgebung mit Docker Compose – vollständiger Ablauf
DNMP-Tutorial (Nginx+MySQL+PHP), in 10 Minuten bereit, löst Umgebungsinkonsistenzen im Team
⏱️ Estimated time: 10 min
- 1
Step 1: Problem und Lösung verstehen
Ausgangslage:
• Jeder Entwickler hat eine andere lokale Umgebung
• Neueinsteiger brauchen einen halben Tag Setup, erfahrene Kollegen müssen 1:1 helfen
• Code läuft auf Rechner A, auf Rechner B bricht er ab
• Deployment auf den Server wird zur Lotterie
• PHP-Versionen weichen ab (7.4 vs. 8.1)
• MySQL-Versionen weichen ab (5.7 vs. 8.0)
Lösung:
• DNMP (Docker+Nginx+MySQL+PHP) mit Docker Compose deployen
• In 10 Minuten fertig, einheitliche Umgebung für alle
• Dieselbe Konfiguration im gesamten Team - 2
Step 2: docker-compose.yml konfigurieren und Services starten
Vollständige Konfiguration:
• docker-compose.yml für Nginx, MySQL und PHP
• Netzwerkverbindungen
• Datenpersistenz
• Umgebungsvariablen
• Healthchecks für zuverlässigen Start
10-Minuten-Ablauf:
1) docker-compose.yml anlegen
2) Nginx-, MySQL- und PHP-Services konfigurieren
3) Start: docker-compose up -d
4) Status prüfen: docker-compose ps
5) PHP-App testen: http://localhost aufrufen - 3
Step 3: Häufige Probleme und Best Practices
Typische Fehler:
• Nginx findet den PHP-FPM-Socket nicht
• MySQL-Verbindung schlägt fehl
• PHP-Erweiterung fehlt
• Konfiguration greift nicht
Behebung:
• Konfiguration anhand der Logs anpassen
• Neu starten: docker-compose down, docker-compose up -d
Best Practices:
• Image-Versionen per Tag fixieren
• Datenpersistenz einrichten
• Healthchecks nutzen
• Konfiguration über Umgebungsvariablen
• Images regelmäßig aktualisieren
• Konfiguration dokumentieren
Nächste Schritte:
• Sofort: git clone https://github.com/imeepo/dnmp.git und erste DNMP-Umgebung starten
• Vertiefen: Docker-Compose-Dokumentation für fortgeschrittene Patterns
• Teilen: Kollegen mit manueller LNMP-Installation weitergeben
FAQ
Warum PHP-Umgebungen mit Docker Compose deployen?
• Jeder Entwickler hat eine andere lokale Umgebung
• Neueinsteiger brauchen einen halben Tag Setup
• Code läuft auf Rechner A, auf Rechner B nicht
• Deployment auf den Server wird zur Lotterie
• PHP-Versionen weichen ab (7.4 vs. 8.1)
• MySQL-Versionen weichen ab (5.7 vs. 8.0)
Lösung: DNMP mit Docker Compose in einem Schritt – in 10 Minuten, alle nutzen dieselbe Konfiguration.
Wie deployt man DNMP mit Docker Compose?
• docker-compose.yml mit Nginx, MySQL und PHP
• Netzwerk, Persistenz, Umgebungsvariablen, Healthchecks
Ablauf:
1) docker-compose.yml anlegen
2) Services konfigurieren
3) docker-compose up -d
4) docker-compose ps
5) http://localhost testen
Welche typischen Probleme gibt es beim DNMP-Deployment?
• Nginx findet PHP-FPM-Socket nicht
• MySQL-Verbindung fehlgeschlagen
• PHP-Erweiterung fehlt
• Konfiguration unwirksam
Behebung:
• Logs lesen, Konfiguration anpassen
• docker-compose down && docker-compose up -d
Diagnose:
• docker-compose logs
• docker-compose ps
• docker network inspect
• Konfigurationsdateien prüfen
Was sind Best Practices für Docker-Compose-PHP-Umgebungen?
• Image-Versionen per Tag fixieren
• Datenpersistenz
• Healthchecks
• Umgebungsvariablen für Secrets und Ports
• Images aktuell halten
• Konfiguration dokumentieren
Nächste Schritte:
• git clone https://github.com/imeepo/dnmp.git
• Docker-Compose-Dokumentation vertiefen
• Mit Kollegen teilen, die noch manuell installieren
9 Min. Lesezeit · Veröffentlicht am: 18. Dez. 2025 · Aktualisiert am: 14. Juli 2026
Docker Praxisleitfaden
Wenn du über die Suche hier gelandet bist, kommst du am schnellsten weiter, indem du zum vorherigen oder nächsten Beitrag dieser Serie springst.
Vorheriger
Docker Compose in Produktion: Healthcheck, Restart-Policy und Ressourcenlimits
Die drei Säulen für stabiles Docker-Compose-Deployment: Healthchecks für echte Bereitschaft, Restart-Policies für Self-Healing und Ressourcenlimits gegen unkontrolliertes Wachstum – inklusive vollständiger YAML-Vorlage.
Teil 12 von 38
Nächster
Docker-Volume-Praxisleitfaden: 5 Beispiele gegen Datenverlust in Containern
Mit 5 Praxisbeispielen lernen Sie Docker Volumes Schritt für Schritt – von den Grundlagen bis zur MySQL- und Redis-Persistenz. So verschwinden Container-Daten nicht mehr beim Löschen. Ideal für Docker-Einsteiger und Entwickler.
Teil 14 von 38
Ähnliche Beiträge
Docker vs. virtuelle Maschine: Performance-Unterschiede und Szenario-Auswahl in 5 Minuten

Docker vs. virtuelle Maschine: Performance-Unterschiede und Szenario-Auswahl in 5 Minuten
Docker-Installation ohne Fallstricke (2025): Vollständige Lösungen von permission denied bis zum erfolgreichen Start


Kommentare
Melde dich mit GitHub an, um einen Kommentar zu hinterlassen