PHP OPcache beschleunigt meine Skripte, weil PHP den kompilierten Bytecode im Speicher hält und damit das erneute Parsen spart. In diesem Leitfaden zeige ich, wie ich OPcache konfiguriere, überwache und fein abstimme, damit Ihre Anwendung messbar schneller reagiert und Lastspitzen gelassen abfängt.
Zentrale Punkte
- Bytecode-Cache senkt CPU-Last und I/O
- Parameter wie memory_consumption und max_accelerated_files gezielt wählen
- Umgebungen differenziert einstellen: Dev, Staging, Produktion
- Monitoring für Hitrate, Belegung, Evictions nutzen
- Deployment und Cache-Flush sauber verzahnen
So arbeitet OPcache: Bytecode statt Re-Compile
Bei jeder Anfrage liest PHP normalerweise Dateien ein, parst den Code und erstellt Bytecode, den die Zend Engine ausführt. OPcache setzt genau hier an und legt diesen Bytecode im Shared Memory ab, damit Folgeanfragen direkt aus dem Speicher starten. Dadurch sinken CPU-Zyklen und Dateizugriffe, was Antwortzeiten spürbar verkürzt. In typischen Setups erreiche ich damit Zugewinne zwischen 30 und 70 Prozent, je nach Codebasis und Traffic-Profil. Entscheidend ist, dass der Cache groß genug bleibt und die wichtigsten Skripte dauerhaft im Speicher bleiben.
OPcache prüfen und aktivieren auf Linux, Windows und Shared-Hosting
Ich starte immer mit einem Blick in phpinfo() und suche nach „Zend OPcache“ sowie Schlüsseln wie opcache.enable oder opcache.memory_consumption. Unter Linux aktiviere ich das Modul über das Paket php-opcache und eine opcache.ini im conf.d-Verzeichnis. Auf Windows reicht ein Eintrag zend_extension=opcache in der php.ini und ein Neustart des Webservers. Im Shared-Hosting schalte ich OPcache oft über eine benutzerdefinierte php.ini oder über das Kundenmenü zu. Bei Engpässen prüfe ich außerdem das PHP memory limit erhöhen, damit OPcache und PHP-FPM genug Ressourcen erhalten.
Die wichtigsten Schalter verständlich erklärt
Mit opcache.enable aktiviere ich den Cache für Webanfragen, während opcache.enable_cli die Nutzung für CLI-Jobs steuert, was sich bei Worker-Queues lohnt. Den Kern bildet opcache.memory_consumption, das den verfügbaren Shared-Memory in Megabyte vorgibt; zu knapp bemessen führt zu Evictions und erneuten Kompilierungen. opcache.max_accelerated_files definiert, wie viele Dateien überhaupt im Cache landen dürfen; dieser Wert sollte die Dateianzahl des Projekts sinnvoll übertreffen. Mit opcache.validate_timestamps und opcache.revalidate_freq bestimme ich, wie streng OPcache Änderungen an Dateien prüft, von sehr dynamisch (Entwicklung) bis sehr sparsam (Produktion mit manuellem Flush). Kommentare sichere ich mit opcache.save_comments=1, weil viele Tools auf DocBlocks angewiesen sind.
Startwerte und Profile im Vergleich
Für einen reibungslosen Einstieg setze ich auf klare Profile für Entwicklung, Staging und Produktion. So erhalte ich einerseits schnelle Feedback-Zyklen beim Coden und andererseits verlässliche Performance im Livebetrieb. Wichtig bleibt, dass Sie diese Startwerte regelmäßig gegen echte Metriken gegenprüfen und nachschärfen. Bei größeren WordPress-Installationen plane ich großzügig Speicher und Einträge ein, weil Plugins und Themes viele Dateien erzeugen. Die folgende Tabelle fasst sinnvolle Ausgangswerte zusammen, die ich anschließend anhand von Hitrate und Evictions feineinstelle.
| Einstellung | Entwicklung | Staging/Test | Produktion |
|---|---|---|---|
| opcache.enable | 1 | 1 | 1 |
| opcache.enable_cli | 0 | 0–1 | 1 (bei CLI-Jobs) |
| opcache.memory_consumption | 128–256 MB | 256–512 MB | 256–512+ MB |
| opcache.interned_strings_buffer | 16–32 MB | 32–64 MB | 16–64 MB |
| opcache.max_accelerated_files | 8.000–10.000 | 10.000–20.000 | 10.000–20.000+ |
| opcache.validate_timestamps | 1 | 1 | 0–1 (abh. von Deploy) |
| opcache.revalidate_freq | 0–2 s | 60–300 s | 300+ s oder 0 (mit manueller Prüfung) |
| opcache.save_comments | 1 | 1 | 1 |
| opcache.fast_shutdown | 1 | 1 | 1 |
Diese Matrix ist bewusst pragmatisch, weil reale Projekte sehr unterschiedlich wachsen. Ich starte mit diesen Werten und beobachte dann die Hitrate, den belegten Anteil am Shared Memory und das Auftreten von Evictions. Bei Anzeichen für Druck erhöhe ich zuerst opcache.memory_consumption in moderaten Schritten. Danach passe ich opcache.max_accelerated_files an, bis die Dateianzahl komfortabel hineinpasst. So bleibt der Cache wirkungsvoll und Anfragen bleiben gleichmäßig flott.
Einstellungen nach Umfeld: Entwicklung, Staging, Produktion
In der Entwicklung zählen schnelle Rückmeldungen auf Codeänderungen, daher setze ich validate_timestamps=1 und revalidate_freq sehr niedrig oder sogar 0. Auf Staging prüfe ich realistische Last und setze Speicher großzügig an, damit die Ergebnisse dem späteren Livebetrieb nahekommen. In der Produktion schiebe ich die Prüffrequenz nach oben oder deaktiviere Timestamps ganz, wenn mein Deployment den Cache danach gezielt leert. Für CLI-basierte Worker aktiviere ich enable_cli=1, damit wiederkehrende Jobs ebenfalls vom Bytecode-Cache profitieren. So erzeugt jede Umgebung genau das Verhalten, das ich brauche, ohne Überraschungen bei Reaktionszeiten.
Erweiterte Einstellungen, die oft den Unterschied machen
Über die Basisparameter hinaus gibt es Schalter, mit denen ich Stabilität und Sicherheit erhöhe und Nebeneffekte minimiere:
- opcache.max_wasted_percentage: Definiert, ab welchem Fragmentierungsgrad OPcache eine interne Neuerstellung des Speichers anstößt. Bei stark wechselnden Codebasen reduziere ich den Wert leicht, um weniger „verschnittenen“ Speicher zu haben.
- opcache.force_restart_timeout: Zeitraum in Sekunden, nach dem OPcache einen erzwungenen Neustart durchführt, wenn ein Restart ansteht, aber Prozesse noch aktiv sind. Das verhindert sehr lange Schwebezustände.
- opcache.file_update_protection: Schutzfenster in Sekunden, in dem frisch veränderte Dateien nicht sofort gecacht werden. Das hilft gegen halb geschriebene Dateien während Deployments oder auf Netzlaufwerken.
- opcache.restrict_api: Beschränkt, welche Skripte opcache_reset() und Statusfunktionen aufrufen dürfen. In Produktion setze ich das strikt, damit nur Administrationsendpunkte Zugriff haben.
- opcache.blacklist_filename: Datei, in der ich Muster pflege, die vom Cache ausgeschlossen werden (z. B. hochdynamische Generatoren). Das spart Platz für kritischere Skripte.
- opcache.validate_permission und opcache.validate_root: Aktiv, wenn mehrere Benutzer/Chroots im Spiel sind. So verhindert PHP, dass gecachter Code aus einem Kontext in einem anderen unerlaubt genutzt wird.
- opcache.use_cwd und opcache.revalidate_path: Steuerung, wie OPcache Skripte identifiziert, wenn Pfade über unterschiedliche Arbeitsverzeichnisse/Symlinks eingebunden werden. Bei Release-Symlinks teste ich diese Werte gezielt, um Doppel-Caches zu vermeiden.
- opcache.cache_id: Wenn mehrere virtuelle Hosts denselben SHM teilen (selten), trenne ich die Caches sauber über eine eindeutige ID.
- opcache.optimization_level: Lasse ich in der Regel auf Standard. Nur bei Debugging-Edgecases reduziere ich Optimierungspässe temporär.
Preloading: Teilen des Codes permanent im Speicher halten
Mit PHP 7.4+ kann ich über opcache.preload und opcache.preload_user zentrale Framework- oder Projektdateien beim Serverstart laden und verknüpfen. Der Vorteil: Klassen stehen ohne Autoload-Hits bereit, und Hot Paths sind sofort verfügbar. Ein paar Praxisregeln:
- Preloading lohnt sich vor allem bei großen, stabilen Codebasen (z. B. Symfony, eigene Kernbibliotheken). Bei WordPress setze ich es zurückhaltend ein, weil Core/Plugins häufiger aktualisiert werden.
- Ein Preload-File enthält gezielte opcache_compile_file()-Aufrufe oder bindet einen Autoloader ein, der definierte Klassen vorab lädt.
- Jede Codeänderung an preload-relevanten Dateien erfordert einen PHP-FPM-Neustart, damit der Preload neu aufgebaut wird. Das binde ich in Deployments fest ein.
- Ich messe die Wirkung separat: nicht jeder Code profitiert; Preloading verbraucht zusätzlich Shared Memory.
JIT und OPcache: Nutzen, Grenzen, Speicherbedarf
Seit PHP 8 existiert der Just-In-Time-Compiler (JIT), der über OPcache gesteuert wird (opcache.jit, opcache.jit_buffer_size). Für typische Web-Workloads mit I/O- und Datenbanklast bringt JIT oft wenig. Bei stark CPU-lastigem Code (z. B. Bild-/Datenverarbeitung) kann er spürbar helfen. Ich gehe so vor:
- Ich aktiviere JIT konservativ und messe Real-User-Metriken sowie CPU-Profile. Blindes Aktivieren vergrößert den Speicherbedarf und kann Edgecases auslösen.
- Den JIT-Buffer dimensioniere ich abhängig von CPU-lastigen Routen. Zu kleine Puffer bringen keinen Mehrwert, zu große verdrängen Bytecode.
- Wenn die Hitrate oder SHM-Belegung leidet, priorisiere ich OPcache vor JIT. Bytecode-Cache ist für die meisten Sites der wichtigere Hebel.
Dateipfade, Symlinks und sichere Deploy-Strategien
OPcache ist pfadbasiert. Deshalb rücke ich die Deploy-Strategie in den Mittelpunkt:
- Atomic Releases per Symlink (z. B. /releases/123 -> /current): Sauber, aber Achtung auf opcache.use_cwd und realpath-Verhalten. Ich vermeide doppelte Caches, indem alle Worker konsistent denselben echten Pfad sehen.
- Mit validate_timestamps=0 muss der Cache überall geleert werden: nach dem Umschalten flushe ich OPcache gezielt auf allen Hosts/Pods und rolle PHP-FPM kontrolliert neu.
- realpath_cache_size und realpath_cache_ttl stimme ich mit OPcache ab, damit Dateilookups schnell und stabil bleiben.
- Auf Netzlaufwerken (NFS/SMB) erhöhe ich file_update_protection und gestalte Deployments so, dass Dateien atomar ersetzt werden.
Für sehr schnelle Restarts setze ich häufig ein zweistufiges Vorgehen ein: erst Warmup im Hintergrund, dann kurzer, koordinierter Reload aller Worker, damit der erste Live-Traffic bereits warmen Cache vorfindet.
File-Cache, Warmup und Priming
Neben dem Shared Memory kann OPcache Bytecode optional auf die Platte schreiben (opcache.file_cache). Das hilft in speziellen Szenarien:
- In Container-Umgebungen kann ein File-Cache zwischen FPM-Neustarts Re-Compile-Zeiten reduzieren, sofern das Storage schnell ist.
- Ich nutze opcache.file_cache vorsichtig: Auf langsamen oder verteilten Filesystemen bringt es wenig und erhöht Komplexität.
- opcache.file_cache_only ist ein Spezialfall für Umgebungen ohne SHM – für Performance-Setups ungebräuchlich.
Für Warmups baue ich mir kleine „Primer“:
- Ein CLI-Skript ruft opcache_compile_file() für Hot Files auf, z. B. Autoloader, zentrale Framework-Klassen, große Helpers.
- Ein Crawler besucht Top-Routen (Homepage, Login, Checkout), damit Bytecode und nachgelagerte Caches rechtzeitig warm sind.
- Ich time Warmups so, dass sie kurz vor dem Umschalten der Version fertig sind.
OPcache im Stack: PHP-FPM, Object-Cache und Page-Cache
OPcache zeigt seine Stärke vor allem in Kombination mit PHP-FPM, einer sauberen Prozesskonfiguration und zusätzlichen Cache-Schichten. Bei WordPress kombiniere ich ihn mit einem Object-Cache (etwa Redis) und einem Page-Cache, damit Datenbank und Rendering entlastet werden. Dazu achte ich auf die Single-Thread-Performance, weil PHP-Anfragen stark von einzelnen CPU-Kernen leben. Entsteht dennoch Druck, verteile ich Last über PHP-FPM-Worker, ohne den Shared Memory von OPcache zu klein zu wählen. So nutze ich den Stack komplett, statt nur an einer Stellschraube zu drehen.
Häufige Fehler und schnelle Checks
Ein zu kleiner Cache führt zu Evictions, die ich im OPcache-Status oder phpinfo() erkenne. Tritt das auf, erhöhe ich schrittweise opcache.memory_consumption und kontrolliere die Wirkung über die Hitrate. Bleiben Dateien unbeschleunigt, setze ich opcache.max_accelerated_files höher als die tatsächliche Dateimenge im Projekt. Bei Deployment-Problemen prüfe ich validate_timestamps: Mit 0 bleibt alter Bytecode aktiv, bis ich den Cache explizit leere. Tools wie Doctrine verlangen DocBlocks, daher lasse ich save_comments=1, um Fehler durch fehlende Annotationen zu vermeiden.
OPcache überwachen und interpretieren
Ich messe die Hitrate und strebe dauerhaft Werte nahe 100 Prozent an, damit Anfragen fast immer aus dem Cache starten. Zusätzlich beobachte ich die Speicherauslastung und die Anzahl der Evictions, um Engpässe früh zu erkennen. Mit opcache_get_status() erzeuge ich mir kleine Dashboards oder füttere bestehende Monitoring-Lösungen. So sehe ich Veränderungen nach Releases oder Plugin-Updates sofort im Trend. Mit diesen Metriken treffe ich fundierte Entscheidungen und passe nur an, was wirklich nötig ist.
Konkrete Leitplanken, die sich bewährt haben:
- Hitrate > 99 % unter Normal- und Peak-Last; darunter prüfe ich Dateiverteilung und Warmup.
- Freier SHM-Anteil konstant > 5–10 %; sonst skaliere ich den Speicher.
- Evictions im Zeitverlauf: Einmalige Ausschläge nach Deploy sind okay; kontinuierliche Evictions deuten auf Unterdimensionierung oder starke Fragmentierung hin.
- Wasted Memory im Auge behalten: Steigt er an die Grenze, plane ich eine kontrollierte OPcache-Neuerstellung (z. B. in Wartungsfenstern).
Beispiel: WordPress-Setup mit hohem Traffic
Für große WordPress-Websites wähle ich opcache.enable=1 und opcache.enable_cli=1, damit auch CLI-Worker profitieren. Den Shared Memory setze ich gerne auf 384 MB oder höher, wenn viele Plugins und ein funktionsreiches Theme beteiligt sind. opcache.interned_strings_buffer erhöhe ich auf 64 MB, weil viele Klassen- und Funktionsnamen über alle Requests wiederkehren. Für extrem performante Umgebungen setze ich validate_timestamps=0 und revalidate_freq=0, flushe den Cache aber direkt nach jedem Release. Wichtig bleibt, Deployments so zu gestalten, dass kein alter Bytecode im Umlauf bleibt.
Praxis-Workflow für Tuning und Deployments
Ich arbeite in festen Zyklen: messen, ändern, kontrollieren. Zuerst sichere ich Statuswerte wie Hitrate, Belegung und Evictions, danach justiere ich einen Parameter und messe erneut. Vor einem Release lösche ich bei deaktivierten Timestamps den OPcache gezielt, entweder über PHP-FPM-Neustart oder ein kleines Skript. Anschließend kontrolliere ich Lastspitzen mit echtem Traffic oder repräsentativen Benchmarks. Tritt auffälliges Verhalten auf, prüfe ich auch Memory-Fragmentierung, weil sie den nutzbaren Shared Memory schmälert.
Ein paar zusätzliche Routinen, die sich in Teams bewährt haben:
- Parameteränderungen versionieren: opcache.ini ins Repo, Änderungen per Pull Request und Changelog.
- Canary-Deploys: Erst ein Teil der Worker/Pods lädt neue Versionen und baut Cache auf, dann Rollout auf alle Instanzen.
- Notfall-Schalter: Ein interner Admin-Endpunkt mit sicherem Zugriff, der opcache_reset() und gezielte opcache_invalidate()-Aufrufe erlaubt – kombiniert mit opcache.restrict_api.
- Größenordnung abschätzen: Als grobe Faustformel kalkuliere ich initial 1–2 MB OPcache pro 100–200 PHP-Dateien und passe danach anhand der realen Metriken an. Für WordPress mit vielen Plugins addiere ich Puffer.
Kurz zusammengefasst
OPcache macht PHP-Anwendungen schneller, indem es den kompilierten Bytecode im RAM vorhält. Mit passenden Einstellungen für Speicher, Datei-Anzahl und Timestamp-Strategie erreichen Sie konstant kurze Antwortzeiten. Achten Sie auf die Abstimmung mit PHP-FPM und weiteren Cache-Schichten, damit der gesamte Stack sauber zusammenarbeitet. Überwachen Sie Hitrate, Belegung und Evictions, damit Sie Anpassungen zielgerichtet vornehmen. So sichern Sie sich eine performante, verlässliche Plattform für hohe Last und Wachstum.


