Design wechseln

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

Easton editorial illustration: service mesh rail yard

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.

10 Min.
Bereitstellungszeit
Ein-Klick-Deployment
100 %
Umgebungskonsistenz
Gleiche Konfiguration im Team
Niedrig
Konfigurationsaufwand
Start mit docker-compose

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:

  1. Nginx installieren (20 Min.)
  2. PHP inkl. Extensions (30 Min., oft kompilieren)
  3. MySQL (15 Min.)
  4. Services verbinden (1 Stunde, viele Stolpersteine)
  5. 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-fpmimage: 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:

  1. Browser → localhost:80
  2. Nginx erkennt PHP-Datei
  3. Weiterleitung an PHP über Port 9000 (Service-Name php)
  4. PHP verbindet bei Bedarf mit MySQL (Service-Name mysql)
  5. Antwort zurück an Nginx
  6. 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_pass
  • php/Dockerfile: Extensions wie mysqli, redis
  • mysql/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 – MySQL Up?
  • host muss mysql sein, nicht localhost
  • Passwort mit .env abgleichen

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):

  1. git clone ...
  2. cp .env.example .env
  3. Passwort in .env setzen
  4. docker-compose up -d
  5. docker 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 Forbidden in Nginx
  • Permission denied beim 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 = localhost oder 127.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:

  1. Zeit sparen: ca. 10 Minuten statt halber Tag Setup
  2. Weniger Bugs: einheitliche Umgebung, kein „bei mir läuft es“ um 2 Uhr nachts
  3. 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:

  1. docker-compose logs
  2. GitHub Issues durchsuchen
  3. 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. 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. 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. 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?
Ausgangslage:
• 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?
Konfiguration:
• 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?
Häufig:
• 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?
Empfehlungen:
• 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

Ähnliche Beiträge

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog