Ein Bind-Mount, der OPcache abschaltete
Der php-Dienst mountete apps/. Jeder Request machte stat() über virtiofs. OPcache-Hits waren Rauschen.
Lokales TTFB im Katalog war 80 ms. Dasselbe Image auf dem VPS, dieselben Daten, lag bei 40 ms, bis ich die Compose-Datei kopierte und apps/ für einen Hotfix mountete. TTFB sprang auf 400 ms. CPU im php-Container war User-Zeit auf stat, nicht auf SQL. opcache_get_status() zeigte Misses in Tausendern und Hits in Zehnern.
opcache.validate_timestamps war 1, revalidate_freq 2. Für ein Laptop ist das richtig. Auf virtiofs prüft jedes include mtime durch die VM. LaraBoom plus Host-App sind viele Dateien. Der Worker zahlte diese Steuer bei jedem Request und lief dann erst in die Liste.
Das Problem war ein Bind-Mount, der OPcache zur Stat-Schleife machte. Besucher warteten 400 ms auf HTML, das 40 brauchte. Ich brauchte COPY im Image, validate_timestamps 0 und ein Rebuild für PHP-Änderungen.
Zwei Compose-Dateien
Der öffentliche php-Dienst COPY vendor, packages/laraboom und apps/ ins Image. validate_timestamps ist 0. opcache.preload lädt Framework und Host-App. Eine PHP-Änderung ist ein neues Image. Der vue-Dienst bindet frontend/ weiter, weil SSR Node ist und wir Vue dort editieren.
- Dev-Compose behält Bind-Mount und Timestamps. Das ist der Sinn lokaler Edits.
- opcache.file_cache auf einem Volume wirkte klug. Es macht trotzdem stat() auf die Quellen, wenn Timestamps an sind.
- nginx und php teilen nur das Unix-Socket-Volume, nicht den PHP-Baum.
TTFB auf dem VPS ging zurück auf die Zahl ohne Mount. Der Hotfix ist ein Rebuild des php-Images. Das ist langsamer als ein Bind. Es ist auch der Prozess, in dem OPcache seine Arbeit tut.
Was ich daraus mitnehme
opcache_get_status() Hits gegen Misses erzählen schneller als ein Profiler. Tausende Misses auf einem warmen Worker sind Timestamps plus Mount.
Dev- und Prod-Compose müssen sich am PHP-Baum unterscheiden. Den Laptop-Mount auf den VPS zu kopieren macht aus 40 ms 400.
Ein PHP-Hotfix ist ein Image-Rebuild. Das ist der Preis für OPcache, der wirklich cached.
