in Containern einrichten
Hot Reload in Containern verhält sich anders als auf dem nackten Host, weil Bind Mounts, Dateisystem-Events und OPcache-Validierung zusätzliche Schichten zwischen Editor und laufendem Prozess einziehen. Wer diese Schichten versteht, bekommt PHP- und Node-Änderungen genauso schnell sichtbar wie ohne Container, ohne bei jeder Codeänderung neu zu bauen.
Inhaltsverzeichnis
- 1. Warum Hot Reload in Containern schwieriger ist
- 2. Bind Mounts richtig für PHP-FPM konfigurieren
- 3. OPcache-Validierung für sofortige Codeänderungen
- 4. Node, Vite und Polling für Dateisystem-Events
- 5. Nginx und PHP-FPM ohne Container-Neustart neu laden
- 6. Docker Compose Watch für automatisches Sync und Rebuild
- 7. Hot Reload auf macOS und Windows in der Praxis
- 8. Hot-Reload-Probleme systematisch debuggen
- 9. Hot-Reload-Ansätze im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Hot Reload in Containern schwieriger ist
Hot Reload bedeutet, dass eine Codeänderung im Editor ohne manuellen Neustart eines Prozesses sofort in der laufenden Anwendung sichtbar wird. Auf dem nackten Host funktioniert das meist reibungslos, weil Editor, Dateisystem und ausführender Prozess denselben Kernel und dieselbe Dateisystem-Schicht teilen. In einem Container kommen zusätzliche Schichten dazu: der Bind Mount zwischen Host und Container, gegebenenfalls eine virtualisierte Dateisystem-Bridge auf macOS oder Windows, und interne Caching-Mechanismen wie OPcache bei PHP, die Dateiänderungen erst nach einem bestimmten Intervall überhaupt bemerken.
Der häufigste Frust bei Hot Reload in Containern entsteht, wenn eine dieser Schichten übersehen wird. Ein Entwickler ändert eine PHP-Datei, der Browser zeigt aber weiterhin den alten Stand, weil OPcache mit aktivierter Validierung, aber falschem Intervall arbeitet. Oder ein Vite-Devserver im Container bemerkt Dateiänderungen gar nicht, weil inotify-Events durch den Bind Mount nicht zuverlässig durchgereicht werden, besonders auf macOS.
Dieser Artikel geht Schicht für Schicht durch, was für zuverlässigen Hot Reload bei PHP-FPM und Node in Docker-Containern nötig ist, von korrekt konfigurierten Bind Mounts über OPcache-Einstellungen bis zu Polling-basierten Watchern und dem neueren Compose-Watch-Feature.
2. Bind Mounts richtig für PHP-FPM konfigurieren
Die Grundvoraussetzung für Hot Reload bei PHP ist ein Bind Mount, der den Projektordner vom Host direkt in den Container spiegelt, statt den Code beim Build in das Image zu kopieren. Wird der Code stattdessen mit COPY im Dockerfile fest ins Image eingebacken, sieht der laufende Container Änderungen am Host-Code niemals, ganz unabhängig von OPcache oder anderen Einstellungen. Für die Entwicklung gilt deshalb: Quellcode wird immer gemountet, niemals ins Image kopiert.
# docker-compose.yml — development configuration with source code mounted
services:
php:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- ./src:/var/www/html:cached
- ./docker/php/opcache-dev.ini:/usr/local/etc/php/conf.d/opcache-dev.ini:ro
environment:
PHP_IDE_CONFIG: "serverName=mironsoft-shop"
Die Mount-Option cached ist auf macOS relevant und teilt Docker mit, dass Schreibzugriffe vom Container aus Priorität haben, während Lesezugriffe leicht verzögert propagiert werden dürfen. Für Hot Reload-Zwecke ist diese Verzögerung meist unproblematisch, weil sie im Millisekunden- statt Sekundenbereich liegt und von Nutzern kaum bemerkt wird.
3. OPcache-Validierung für sofortige Codeänderungen
PHP-FPM cacht kompilierten Bytecode standardmäßig über OPcache, was in Produktion die Performance deutlich verbessert, in der Entwicklung aber genau der Grund ist, warum Hot Reload scheinbar nicht funktioniert. Die Einstellung opcache.validate_timestamps steuert, ob PHP bei jedem Request prüft, ob sich die Datei seit dem letzten Caching geändert hat. Ist diese Option deaktiviert, wie in vielen produktionsnahen Konfigurationen üblich, bleiben Codeänderungen bis zu einem manuellen opcache_reset() unsichtbar.
; docker/php/opcache-dev.ini — development-only OPcache settings
; Production should use validate_timestamps=0 with an explicit cache reset on deploy
opcache.enable=1
opcache.validate_timestamps=1
opcache.revalidate_freq=0
opcache.max_accelerated_files=10000
Der Schlüssel opcache.revalidate_freq=0 ist für Hot Reload entscheidend: er sorgt dafür, dass PHP bei jedem einzelnen Request die Timestamp-Validierung durchführt, statt sie nur alle paar Sekunden zu wiederholen. In Produktion würde dieser Wert die Performance messbar verschlechtern, in der lokalen Entwicklung ist der Unterschied bei einem einzelnen Entwickler-Request nicht spürbar, macht aber den entscheidenden Unterschied zwischen sofort sichtbaren und scheinbar hängenden Änderungen.
4. Node, Vite und Polling für Dateisystem-Events
Node-basierte Devserver wie Vite oder Webpack Dev Server nutzen normalerweise native Dateisystem-Events wie inotify unter Linux, um Änderungen sofort zu erkennen. Innerhalb eines Containers mit Bind Mount funktionieren diese Events auf nativem Linux zuverlässig, auf macOS und teilweise unter Windows jedoch nicht immer, weil die zugrunde liegende Dateisystem-Bridge inotify-Events nicht immer korrekt durchreicht. Das Ergebnis: der Devserver merkt Dateiänderungen erst nach einem manuellen Neustart, was Hot Reload praktisch unbrauchbar macht.
{
"scripts": {
"dev": "vite --host 0.0.0.0"
}
}
// vite.config.js — enable polling as a fallback for unreliable fs events
export default {
server: {
watch: {
// Polling trades some CPU usage for reliability across bind mounts
usePolling: true,
interval: 300
},
host: true,
hmr: {
// Explicit port so HMR websocket works through the container's port mapping
port: 5173,
clientPort: 5173
}
}
}
Die Umgebungsvariable CHOKIDAR_USEPOLLING=true wirkt ähnlich für Tools, die intern die Chokidar-Bibliothek nutzen, etwa ältere Webpack-Setups. Polling erkauft die Zuverlässigkeit von Hot Reload mit etwas höherem CPU-Verbrauch, weil das Dateisystem in festen Intervallen aktiv abgefragt statt passiv auf Events gewartet wird, was für Entwicklungsumgebungen ein akzeptabler Kompromiss ist.
5. Nginx und PHP-FPM ohne Container-Neustart neu laden
Änderungen an PHP-Dateien selbst brauchen dank OPcache-Validierung keinen Neustart, aber Änderungen an der PHP-FPM- oder Nginx-Konfiguration schon, wenn keine Vorkehrungen getroffen werden. Für Hot Reload-freundliche Entwicklung lohnt sich, PHP-FPM mit einem Reload-Signal statt einem vollständigen Neustart zu betreiben, was laufende Requests nicht unterbricht und deutlich schneller ist als ein kompletter Container-Neustart.
# Reload PHP-FPM configuration without dropping active connections
docker compose exec php kill -USR2 1
# Reload nginx configuration the same way
docker compose exec nginx nginx -s reload
Das Signal USR2 an PHP-FPM startet die Worker-Prozesse graceful neu, ohne aktive Requests zu unterbrechen, und übernimmt dabei automatisch geänderte Pool-Konfigurationen. Für den täglichen Hot Reload-Workflow reicht das in den seltenen Fällen, in denen tatsächlich die FPM-Konfiguration selbst geändert wurde, während reine PHP-Codeänderungen ohnehin ohne jeden Neustart auskommen.
6. Docker Compose Watch für automatisches Sync und Rebuild
Seit neueren Docker-Compose-Versionen steht mit develop.watch ein deklaratives Feature zur Verfügung, das Hot Reload ohne zusätzliche externe Tools ermöglicht. Statt eines vollständigen Bind Mounts synchronisiert Compose Watch geänderte Dateien gezielt in den Container, kann bei bestimmten Dateitypen einen automatischen Rebuild auslösen und unterscheidet zwischen reinem Datei-Sync und vollständigem Neubau.
# docker-compose.yml — declarative hot reload with Compose Watch
services:
app:
build: .
develop:
watch:
- action: sync
path: ./src
target: /var/www/html
- action: rebuild
path: ./composer.json
- action: sync+restart
path: ./docker/php/php.ini
target: /usr/local/etc/php/php.ini
# Start the stack with watch mode enabled
docker compose watch
Die Aktion sync kopiert geänderte Dateien direkt in den laufenden Container, ohne einen Neustart auszulösen, ideal für PHP-Dateien mit aktivierter OPcache-Validierung. Die Aktion rebuild baut das Image komplett neu, sinnvoll bei Änderungen an composer.json oder package.json, während sync+restart eine Datei synchronisiert und danach gezielt den betroffenen Service neu startet. Diese drei Modi decken die meisten Hot Reload-Szenarien ab, ohne dass ein externes Sync-Tool wie Mutagen nötig wäre.
7. Hot Reload auf macOS und Windows in der Praxis
Auf macOS und unter WSL2 auf Windows bringt die virtualisierte Dateisystem-Bridge zwischen Host und Container zusätzliche Latenz mit, die sich direkt auf die gefühlte Geschwindigkeit von Hot Reload auswirkt. Mit aktuellen Mount-Strategien wie virtiofs auf macOS reduziert sich diese Latenz auf ein für die tägliche Entwicklung kaum wahrnehmbares Maß, ältere Mount-Treiber wie osxfs oder gRPC-FUSE führten hier früher zu spürbaren Verzögerungen von mehreren Sekunden.
Wichtig für Hot Reload speziell auf diesen Plattformen: Node-Devserver sollten praktisch immer mit aktiviertem Polling laufen, weil native Dateisystem-Events über die virtualisierte Bridge unzuverlässig ankommen, selbst mit modernen Mount-Treibern. PHP-Änderungen über OPcache-Validierung sind von dieser Problematik weniger betroffen, weil PHP-FPM Dateien bei jedem Request aktiv prüft, statt auf passive Events zu warten.
8. Hot-Reload-Probleme systematisch debuggen
Wenn Hot Reload scheinbar nicht funktioniert, hilft eine systematische Fehlersuche entlang der beteiligten Schichten, statt wahllos Konfigurationswerte zu ändern. Der erste Schritt ist immer, zu prüfen, ob die Dateiänderung überhaupt im Container ankommt, unabhängig von Caching oder Watchern.
#!/usr/bin/env bash
# debug-hot-reload.sh — verify each layer independently
set -euo pipefail
echo "1) Does the change reach the container at all?"
docker compose exec app stat -c '%Y %n' /var/www/html/src/Controller/IndexController.php
echo "2) Is OPcache actually caching (and configured to revalidate)?"
docker compose exec app php -i | grep -E "opcache.(enable|validate_timestamps|revalidate_freq)"
echo "3) Is the Node dev server watching at all, or silently stuck?"
docker compose logs app-frontend --tail=20
echo "4) Are filesystem events reaching the container (Linux only)?"
docker compose exec app-frontend sh -c "command -v inotifywait && inotifywait -m /var/www/html/resources/js"
Diese vier Schritte trennen zuverlässig, ob das Problem beim Bind Mount, bei OPcache, beim Node-Watcher oder bei den zugrunde liegenden Dateisystem-Events liegt. Die meisten Hot Reload-Probleme lassen sich mit dieser Reihenfolge in wenigen Minuten eingrenzen, statt stundenlang Konfigurationsdateien durchzuprobieren.
9. Hot-Reload-Ansätze im Vergleich
Für unterschiedliche Technologien und Plattformen eignen sich unterschiedliche Kombinationen der vorgestellten Techniken.
| Szenario | Empfohlener Ansatz | Latenz | CPU-Kosten |
|---|---|---|---|
| PHP auf Linux | Bind Mount plus OPcache-Validierung | Sehr niedrig | Vernachlässigbar |
| Node auf Linux | Native fs-Events, kein Polling nötig | Sehr niedrig | Niedrig |
| Node auf macOS/WSL2 | Polling aktivieren | Niedrig bis mittel | Erhöht |
| Gemischter PHP/Node-Stack | Compose Watch mit sync/rebuild/sync+restart | Niedrig | Moderat |
Für reine PHP-Stacks auf Linux ist die Kombination aus Bind Mount und OPcache-Validierung meist ausreichend und kostet praktisch keine zusätzliche Rechenzeit. Node-Devserver auf macOS oder unter WSL2 brauchen dagegen fast immer Polling, um überhaupt zuverlässig zu funktionieren. Compose Watch ist die derzeit flexibelste Lösung für gemischte Stacks, weil es je nach Dateityp unterschiedlich reagieren kann.
Mironsoft
Docker-Entwicklungsumgebungen, PHP-Performance und Node-Tooling
Codeänderungen ohne Wartezeit sehen wollen?
Wir richten zuverlässigen Hot Reload für eure PHP- und Node-Container ein, von OPcache-Feintuning über Compose Watch bis zur plattformspezifischen Polling-Konfiguration für macOS und WSL2.
OPcache-Tuning
Entwicklungsfreundliche PHP-Konfiguration ohne Performanceverlust in Produktion
Compose Watch Setup
Sync-, Rebuild- und Restart-Regeln passend zu eurem Projekt einrichten
Plattform-Optimierung
Mount-Strategien und Polling-Konfiguration für macOS und Windows/WSL2
10. Zusammenfassung
Zuverlässiger Hot Reload in Containern braucht das Zusammenspiel mehrerer Schichten: ein korrekt konfigurierter Bind Mount bringt Codeänderungen überhaupt erst in den Container, OPcache-Validierung mit revalidate_freq=0 sorgt dafür, dass PHP diese Änderungen bei jedem Request erkennt, und Polling überbrückt die Unzuverlässigkeit nativer Dateisystem-Events bei Node-Devservern auf macOS und Windows. Docker Compose Watch bündelt diese Konzepte inzwischen in einem deklarativen Feature direkt in der Compose-Datei.
Wer Hot Reload-Probleme systematisch statt zufällig löst, prüft zuerst, ob die Änderung überhaupt im Container ankommt, dann die Caching-Schicht, dann den Watcher-Prozess. Diese Reihenfolge spart in der Praxis deutlich mehr Zeit als das wahllose Ausprobieren einzelner Konfigurationswerte und macht Hot Reload in Containern genauso schnell wie eine klassische lokale Installation.
Hot Reload für PHP und Node — Das Wichtigste auf einen Blick
Bind Mounts
Quellcode immer mounten, niemals ins Image kopieren, sonst sieht der Container Host-Änderungen nie.
OPcache
validate_timestamps=1 und revalidate_freq=0 in der Entwicklung, niemals in Produktion.
Polling
Für Node-Devserver auf macOS und WSL2 fast immer nötig, da native fs-Events unzuverlässig sind.
Compose Watch
Deklarative sync-, rebuild- und sync+restart-Regeln direkt in der Compose-Datei ohne externe Tools.