[{"data":1,"prerenderedAt":1563},["ShallowReactive",2],{"navigation":3,"/docs/guide/production-performance-tuning":229,"/docs/guide/production-performance-tuning-surround":1558},[4],{"title":5,"path":6,"stem":7,"children":8},"Docs","/docs","docs",[9,12,56,85,132,153,186,203,216],{"title":10,"path":6,"stem":11},"","docs/index",{"title":13,"path":14,"stem":15,"children":16,"icon":55},"Getting Started","/docs/getting-started","docs/1.getting-started/1.index",[17,19,23,27,31,35,39,43,47,51],{"title":18,"path":14,"stem":15},"Introduction",{"title":20,"path":21,"stem":22},"Container Basics","/docs/getting-started/container-basics","docs/1.getting-started/2.container-basics",{"title":24,"path":25,"stem":26},"Installation","/docs/getting-started/installation","docs/1.getting-started/3.installation",{"title":28,"path":29,"stem":30},"These Images vs Others","/docs/getting-started/these-images-vs-others","docs/1.getting-started/4.these-images-vs-others",{"title":32,"path":33,"stem":34},"Choosing an Image","/docs/getting-started/choosing-an-image","docs/1.getting-started/5.choosing-an-image",{"title":36,"path":37,"stem":38},"Default Configurations","/docs/getting-started/default-configurations","docs/1.getting-started/6.default-configurations",{"title":40,"path":41,"stem":42},"Upgrade Guide","/docs/getting-started/upgrade-guide","docs/1.getting-started/7.upgrade-guide",{"title":44,"path":45,"stem":46},"Changelog","/docs/getting-started/changelog","docs/1.getting-started/8.changelog",{"title":48,"path":49,"stem":50},"About","/docs/getting-started/about","docs/1.getting-started/9.about",{"title":52,"path":53,"stem":54},"Contributing","/docs/getting-started/contributing","docs/1.getting-started/99.contributing",false,{"title":57,"path":58,"stem":59,"children":60,"page":55},"Image Variations","/docs/image-variations","docs/2.image-variations",[61,65,69,73,77,81],{"title":62,"path":63,"stem":64},"CLI","/docs/image-variations/cli","docs/2.image-variations/cli",{"title":66,"path":67,"stem":68},"FPM","/docs/image-variations/fpm","docs/2.image-variations/fpm",{"title":70,"path":71,"stem":72},"FPM-Apache","/docs/image-variations/fpm-apache","docs/2.image-variations/fpm-apache",{"title":74,"path":75,"stem":76},"FPM-NGINX","/docs/image-variations/fpm-nginx","docs/2.image-variations/fpm-nginx",{"title":78,"path":79,"stem":80},"FrankenPHP","/docs/image-variations/frankenphp","docs/2.image-variations/frankenphp",{"title":82,"path":83,"stem":84},"Unit (Deprecated)","/docs/image-variations/unit","docs/2.image-variations/unit",{"title":86,"path":87,"stem":88,"children":89,"page":55},"Framework Guides","/docs/framework-guides","docs/3.framework-guides",[90,123],{"title":91,"icon":55,"defaultOpen":55,"path":92,"stem":93,"children":94,"page":55},"Laravel","/docs/framework-guides/laravel","docs/3.framework-guides/1.laravel",[95,99,103,107,111,115,119],{"title":96,"path":97,"stem":98},"Automations","/docs/framework-guides/laravel/automations","docs/3.framework-guides/1.laravel/1.automations",{"title":100,"path":101,"stem":102},"Task Scheduler","/docs/framework-guides/laravel/task-scheduler","docs/3.framework-guides/1.laravel/2.task-scheduler",{"title":104,"path":105,"stem":106},"Queue","/docs/framework-guides/laravel/queue","docs/3.framework-guides/1.laravel/3.queue",{"title":108,"path":109,"stem":110},"Horizon","/docs/framework-guides/laravel/horizon","docs/3.framework-guides/1.laravel/4.horizon",{"title":112,"path":113,"stem":114},"Reverb","/docs/framework-guides/laravel/reverb","docs/3.framework-guides/1.laravel/4.reverb",{"title":116,"path":117,"stem":118},"Nightwatch","/docs/framework-guides/laravel/nightwatch","docs/3.framework-guides/1.laravel/5.nightwatch",{"title":120,"path":121,"stem":122},"Octane","/docs/framework-guides/laravel/octane","docs/3.framework-guides/1.laravel/octane",{"title":124,"icon":55,"defaultOpen":55,"path":125,"stem":126,"children":127,"page":55},"WordPress","/docs/framework-guides/wordpress","docs/3.framework-guides/2.wordpress",[128],{"title":129,"path":130,"stem":131},"Using Docker with WordPress","/docs/framework-guides/wordpress/using-wordpress-with-docker","docs/3.framework-guides/2.wordpress/4.using-wordpress-with-docker",{"title":133,"path":134,"stem":135,"children":136,"page":55},"Deployment And Production","/docs/deployment-and-production","docs/4.deployment-and-production",[137,141,145,149],{"title":138,"path":139,"stem":140},"Development to Production","/docs/deployment-and-production/development-to-production","docs/4.deployment-and-production/2.development-to-production",{"title":142,"path":143,"stem":144},"Packaging Your App for Deployment","/docs/deployment-and-production/packaging-your-app-for-deployment","docs/4.deployment-and-production/3.packaging-your-app-for-deployment",{"title":146,"path":147,"stem":148},"Configuring SSL","/docs/deployment-and-production/configuring-ssl","docs/4.deployment-and-production/4.configuring-ssl",{"title":150,"path":151,"stem":152},"Choosing a Host","/docs/deployment-and-production/choosing-a-host","docs/4.deployment-and-production/5.choosing-a-host",{"title":154,"icon":55,"defaultOpen":55,"path":155,"stem":156,"children":157,"page":55},"Advanced Guides","/docs/guide","docs/5.guide",[158,162,166,170,174,178,182],{"title":159,"path":160,"stem":161},"Migrating from official PHP images","/docs/guide/migrating-from-official-php-images","docs/5.guide/1.migrating-from-official-php-images",{"title":163,"path":164,"stem":165},"Using Healthchecks With Laravel","/docs/guide/using-healthchecks-with-laravel","docs/5.guide/2.using-healthchecks-with-laravel",{"title":167,"path":168,"stem":169},"Using S6 Overlay","/docs/guide/using-s6-overlay","docs/5.guide/2.using-s6-overlay",{"title":171,"path":172,"stem":173},"Understanding File Permissions","/docs/guide/understanding-file-permissions","docs/5.guide/3.understanding-file-permissions",{"title":175,"path":176,"stem":177},"Configuring Trusted Proxies","/docs/guide/configuring-trusted-proxies","docs/5.guide/4.configuring-trusted-proxies",{"title":179,"path":180,"stem":181},"Major version migrations","/docs/guide/major-version-migrations","docs/5.guide/5.major-version-migrations",{"title":183,"path":184,"stem":185},"Production performance tuning","/docs/guide/production-performance-tuning","docs/5.guide/6.production-performance-tuning",{"title":187,"icon":55,"defaultOpen":55,"path":188,"stem":189,"children":190,"page":55},"Customization","/docs/customizing-the-image","docs/6.customizing-the-image",[191,195,199],{"title":192,"path":193,"stem":194},"Changing php.ini settings","/docs/customizing-the-image/changing-common-php-settings","docs/6.customizing-the-image/1.changing-common-php-settings",{"title":196,"path":197,"stem":198},"Installing PHP extensions","/docs/customizing-the-image/installing-additional-php-extensions","docs/6.customizing-the-image/2.installing-additional-php-extensions",{"title":200,"path":201,"stem":202},"Adding Start Up Scripts","/docs/customizing-the-image/adding-your-own-start-up-scripts","docs/6.customizing-the-image/3.adding-your-own-start-up-scripts",{"title":204,"path":205,"stem":206,"children":207,"page":55},"Troubleshooting","/docs/troubleshooting","docs/7.troubleshooting",[208,212],{"title":209,"path":210,"stem":211},"Common Issues","/docs/troubleshooting/common-issues","docs/7.troubleshooting/1.common-issues",{"title":213,"path":214,"stem":215},"Getting Help","/docs/troubleshooting/getting-help","docs/7.troubleshooting/2.getting-help",{"title":217,"path":218,"stem":219,"children":220,"page":55},"Reference","/docs/reference","docs/8.reference",[221,225],{"title":222,"path":223,"stem":224},"Environment Variable Specification","/docs/reference/environment-variable-specification","docs/8.reference/1.environment-variable-specification",{"title":226,"path":227,"stem":228},"Command Reference","/docs/reference/command-reference","docs/8.reference/2.command-reference",{"id":230,"title":183,"body":231,"description":1550,"extension":1551,"links":1552,"meta":1553,"navigation":711,"path":184,"redirect":1552,"seo":1556,"stem":185,"__hash__":1557},"docs/docs/5.guide/6.production-performance-tuning.md",{"type":232,"value":233,"toc":1536},"minimark",[234,241,246,258,263,273,314,317,382,400,404,410,608,636,643,647,661,664,668,675,1044,1050,1061,1100,1103,1107,1114,1117,1147,1158,1162,1166,1187,1199,1202,1224,1227,1245,1251,1303,1306,1361,1364,1383,1393,1455,1458,1462,1469,1491,1495,1526,1532],[235,236,237],"lead-p",{},[238,239,240],"p",{},"This guide covers the settings that matter for performance once your application is in production, where our defaults come from, and how to tune them when your app needs more.",[242,243,245],"h2",{"id":244},"php-opcache","PHP OPcache",[238,247,248,249,257],{},"OPcache \"improves PHP performance by storing precompiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request\" (",[250,251,256],"a",{"href":252,"rel":253,"target":255},"https://www.php.net/manual/en/book.opcache.php",[254],"nofollow","_blank","PHP manual","). This means that every request skips the work of reading and parsing your PHP files, so it is faster. It also means that changes to your code are not seen until the cache is cleared, which is why we keep it off by default.",[259,260,262],"h3",{"id":261},"turning-it-on","Turning it on",[238,264,265,266,272],{},"OPcache is controlled by one switch, ",[250,267,268],{"href":223},[269,270,271],"code",{},"PHP_OPCACHE_ENABLE",".",[274,275,276,289],"table",{},[277,278,279],"thead",{},[280,281,282,286],"tr",{},[283,284,285],"th",{},"Setting",[283,287,288],{},"What happens",[290,291,292,304],"tbody",{},[280,293,294,301],{},[295,296,297,300],"td",{},[269,298,299],{},"PHP_OPCACHE_ENABLE=0"," (default)",[295,302,303],{},"OPcache is off. Every request reads your files fresh, so edits show up right away when your code is mounted as a volume.",[280,305,306,311],{},[295,307,308],{},[269,309,310],{},"PHP_OPCACHE_ENABLE=1",[295,312,313],{},"OPcache is on with the tuned defaults below. Files are compiled once and cached until the container restarts.",[238,315,316],{},"We keep OPcache off by default so nobody loses an afternoon wondering why a change is not showing up. Turn it on anywhere you are not editing code in place, such as production or staging:",[318,319,326],"pre",{"className":320,"code":321,"filename":322,"highlights":323,"language":325,"meta":10,"style":10},"language-yml shiki shiki-themes github-dark","services:\n  php:\n    image: serversideup/php:8.5-fpm-nginx\n    environment:\n      PHP_OPCACHE_ENABLE: \"1\"\n","compose.yml",[324],5,"yml",[269,327,328,341,349,362,370],{"__ignoreMap":10},[329,330,333,337],"span",{"class":331,"line":332},"line",1,[329,334,336],{"class":335},"s4JwU","services",[329,338,340],{"class":339},"s95oV",":\n",[329,342,344,347],{"class":331,"line":343},2,[329,345,346],{"class":335},"  php",[329,348,340],{"class":339},[329,350,352,355,358],{"class":331,"line":351},3,[329,353,354],{"class":335},"    image",[329,356,357],{"class":339},": ",[329,359,361],{"class":360},"sU2Wk","serversideup/php:8.5-fpm-nginx\n",[329,363,365,368],{"class":331,"line":364},4,[329,366,367],{"class":335},"    environment",[329,369,340],{"class":339},[329,371,374,377,379],{"class":372,"line":324},[331,373],"highlight",[329,375,376],{"class":335},"      PHP_OPCACHE_ENABLE",[329,378,357],{"class":339},[329,380,381],{"class":360},"\"1\"\n",[383,384,385],"note",{},[238,386,387,388,391,392,395,396,399],{},"CLI commands like ",[269,389,390],{},"php artisan"," also use OPcache when ",[269,393,394],{},"PHP_OPCACHE_ENABLE_CLI=1",", which is the default. A CLI process gets its own cache that ends when the process exits, so the gain there is optimized code rather than caching. Set ",[269,397,398],{},"PHP_OPCACHE_ENABLE_CLI=0"," to keep OPcache on for the web server only.",[259,401,403],{"id":402},"defaults","Defaults",[238,405,406,407,409],{},"When ",[269,408,310],{},", these are the defaults you get. Override any of them with the same variable:",[274,411,412,425],{},[277,413,414],{},[280,415,416,419,422],{},[283,417,418],{},"Variable",[283,420,421],{},"Default",[283,423,424],{},"What it controls",[290,426,427,449,464,479,494,509,524,542,561,575,594],{},[280,428,429,434,439],{},[295,430,431],{},[269,432,433],{},"PHP_OPCACHE_VALIDATE_TIMESTAMPS",[295,435,436],{},[269,437,438],{},"0",[295,440,441,442,444,445,448],{},"Whether OPcache checks if a file changed. ",[269,443,438],{}," caches files until the container restarts, so ",[269,446,447],{},"PHP_OPCACHE_REVALIDATE_FREQ"," is never used.",[280,450,451,456,461],{},[295,452,453],{},[269,454,455],{},"PHP_OPCACHE_MEMORY_CONSUMPTION",[295,457,458],{},[269,459,460],{},"256",[295,462,463],{},"Size of the shared memory segment in megabytes.",[280,465,466,471,476],{},[295,467,468],{},[269,469,470],{},"PHP_OPCACHE_INTERNED_STRINGS_BUFFER",[295,472,473],{},[269,474,475],{},"32",[295,477,478],{},"Megabytes inside that segment for interned strings.",[280,480,481,486,491],{},[295,482,483],{},[269,484,485],{},"PHP_OPCACHE_MAX_ACCELERATED_FILES",[295,487,488],{},[269,489,490],{},"32531",[295,492,493],{},"Maximum number of files in the cache.",[280,495,496,501,506],{},[295,497,498],{},[269,499,500],{},"PHP_OPCACHE_FORCE_RESTART_TIMEOUT",[295,502,503],{},[269,504,505],{},"180",[295,507,508],{},"Seconds to wait for a scheduled restart before OPcache forces it.",[280,510,511,516,521],{},[295,512,513],{},[269,514,515],{},"PHP_OPCACHE_SAVE_COMMENTS",[295,517,518],{},[269,519,520],{},"1",[295,522,523],{},"Keep doc comments in the cache. Disabling it \"may break applications and frameworks that rely on comment parsing for annotations, including Doctrine, Zend Framework 2 and PHPUnit.\"",[280,525,526,531,535],{},[295,527,528],{},[269,529,530],{},"PHP_OPCACHE_ENABLE_FILE_OVERRIDE",[295,532,533],{},[269,534,438],{},[295,536,537,538,541],{},"Let OPcache answer ",[269,539,540],{},"file_exists()"," from its cache. Left off because it \"risks returning stale data if opcache.validate_timestamps is disabled.\"",[280,543,544,549,554],{},[295,545,546],{},[269,547,548],{},"PHP_OPCACHE_JIT",[295,550,551],{},[269,552,553],{},"off",[295,555,556,557,272],{},"The JIT compiler. See ",[250,558,560],{"href":559},"#jit","JIT",[280,562,563,568,572],{},[295,564,565],{},[269,566,567],{},"PHP_OPCACHE_JIT_BUFFER_SIZE",[295,569,570],{},[269,571,438],{},[295,573,574],{},"Memory for JIT code.",[280,576,577,582,587],{},[295,578,579],{},[269,580,581],{},"PHP_OPCACHE_PRELOAD",[295,583,584],{},[269,585,586],{},"\"\"",[295,588,589,590,272],{},"Path to a preload script. See ",[250,591,593],{"href":592},"#preloading","Preloading",[280,595,596,601,605],{},[295,597,598],{},[269,599,600],{},"PHP_OPCACHE_PRELOAD_USER",[295,602,603],{},[269,604,586],{},[295,606,607],{},"User to run the preload script as. Only needed when running as root.",[238,609,610,611,614,615,620,621,626,627,632,633,272],{},"These are not our numbers. The memory size, file count, interned strings buffer, and ",[269,612,613],{},"validate_timestamps=0"," are the values in ",[250,616,619],{"href":617,"rel":618,"target":255},"https://symfony.com/doc/current/performance.html#configure-opcache-for-maximum-performance",[254],"Symfony's performance guide",", which ",[250,622,625],{"href":623,"rel":624,"target":255},"https://frankenphp.dev/docs/performance/",[254],"FrankenPHP's performance guide"," recommends \"even if you don't use Symfony.\" Laravel does not publish OPcache values, but ",[250,628,631],{"href":629,"rel":630,"target":255},"https://laravel.com/forge/docs/servers/php#opcache",[254],"Laravel Forge's OPcache option"," asks the same thing of you that this configuration does: reload PHP after every deployment. Every variable links to the PHP manual in the ",[250,634,635],{"href":223},"environment variable specification",[238,637,638,639,642],{},"The most important one is ",[269,640,641],{},"PHP_OPCACHE_VALIDATE_TIMESTAMPS=0",". Symfony's guide puts it plainly: \"In production servers, PHP files should never change, unless a new application version is deployed.\" When your code is built into the image, checking for changes is wasted work.",[259,644,646],{"id":645},"memory","Memory",[238,648,649,650,652,653,655,656,660],{},"OPcache reserves one shared memory segment of ",[269,651,455],{}," megabytes when PHP starts. The interned strings buffer lives inside that segment. With the defaults, 256 MB is reserved, 32 MB of it holds interned strings, and 224 MB holds compiled code. The JIT buffer is different: when the JIT is on, PHP adds ",[269,654,567],{}," on top, so \"its total size is this value plus opcache.jit_buffer_size\" (",[250,657,256],{"href":658,"rel":659,"target":255},"https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.memory-consumption",[254],").",[238,662,663],{},"Reserving memory is not the same as using it. The container only pays for pages that are written, and every PHP-FPM worker shares the same segment.",[259,665,667],{"id":666},"measuring","Measuring",[238,669,670,671,674],{},"PHP reports how full the cache is. Put this file in your public directory and open it in a browser or with ",[269,672,673],{},"curl",". It has to go through the web server, since the CLI has its own cache:",[318,676,681],{"className":677,"code":678,"filename":679,"language":680,"meta":10,"style":10},"language-php shiki shiki-themes github-dark","\u003C?php\nheader('Content-Type: text/plain');\n\n$status = opcache_get_status(false);\n$stats = $status['opcache_statistics'];\n$memory = $status['memory_usage'];\n$strings = $status['interned_strings_usage'];\n$toMegabytes = fn (int $bytes) => round($bytes / 1048576) . ' MB';\n$files = \"{$stats['num_cached_keys']} of {$stats['max_cached_keys']}\";\n\n$report = [\n    'Cache full' => $status['cache_full'] ? 'yes' : 'no',\n    'Cached files' => $files,\n    'Memory used' => $toMegabytes($memory['used_memory']),\n    'Memory free' => $toMegabytes($memory['free_memory']),\n    'Interned strings free' => $toMegabytes($strings['free_memory']),\n    'Out of memory restarts' => $stats['oom_restarts'],\n    'Hash restarts' => $stats['hash_restarts'],\n];\n\nforeach ($report as $label => $value) {\n    echo \"$label: $value\n\";\n}\n","public/opcache-status.php","php",[269,682,683,693,707,713,731,747,762,777,820,844,849,860,892,903,920,935,950,967,982,987,992,1013,1030,1038],{"__ignoreMap":10},[329,684,685,689],{"class":331,"line":332},[329,686,688],{"class":687},"snl16","\u003C?",[329,690,692],{"class":691},"sDLfK","php\n",[329,694,695,698,701,704],{"class":331,"line":343},[329,696,697],{"class":691},"header",[329,699,700],{"class":339},"(",[329,702,703],{"class":360},"'Content-Type: text/plain'",[329,705,706],{"class":339},");\n",[329,708,709],{"class":331,"line":351},[329,710,712],{"emptyLinePlaceholder":711},true,"\n",[329,714,715,718,721,724,726,729],{"class":331,"line":364},[329,716,717],{"class":339},"$status ",[329,719,720],{"class":687},"=",[329,722,723],{"class":691}," opcache_get_status",[329,725,700],{"class":339},[329,727,728],{"class":691},"false",[329,730,706],{"class":339},[329,732,733,736,738,741,744],{"class":331,"line":324},[329,734,735],{"class":339},"$stats ",[329,737,720],{"class":687},[329,739,740],{"class":339}," $status[",[329,742,743],{"class":360},"'opcache_statistics'",[329,745,746],{"class":339},"];\n",[329,748,750,753,755,757,760],{"class":331,"line":749},6,[329,751,752],{"class":339},"$memory ",[329,754,720],{"class":687},[329,756,740],{"class":339},[329,758,759],{"class":360},"'memory_usage'",[329,761,746],{"class":339},[329,763,765,768,770,772,775],{"class":331,"line":764},7,[329,766,767],{"class":339},"$strings ",[329,769,720],{"class":687},[329,771,740],{"class":339},[329,773,774],{"class":360},"'interned_strings_usage'",[329,776,746],{"class":339},[329,778,780,783,785,788,791,794,797,800,803,806,809,812,814,817],{"class":331,"line":779},8,[329,781,782],{"class":339},"$toMegabytes ",[329,784,720],{"class":687},[329,786,787],{"class":687}," fn",[329,789,790],{"class":339}," (",[329,792,793],{"class":687},"int",[329,795,796],{"class":339}," $bytes) => ",[329,798,799],{"class":691},"round",[329,801,802],{"class":339},"($bytes ",[329,804,805],{"class":687},"/",[329,807,808],{"class":691}," 1048576",[329,810,811],{"class":339},") ",[329,813,272],{"class":687},[329,815,816],{"class":360}," ' MB'",[329,818,819],{"class":339},";\n",[329,821,823,826,828,831,834,837,839,842],{"class":331,"line":822},9,[329,824,825],{"class":339},"$files ",[329,827,720],{"class":687},[329,829,830],{"class":360}," \"{",[329,832,833],{"class":339},"$stats",[329,835,836],{"class":360},"['num_cached_keys']} of {",[329,838,833],{"class":339},[329,840,841],{"class":360},"['max_cached_keys']}\"",[329,843,819],{"class":339},[329,845,847],{"class":331,"line":846},10,[329,848,712],{"emptyLinePlaceholder":711},[329,850,852,855,857],{"class":331,"line":851},11,[329,853,854],{"class":339},"$report ",[329,856,720],{"class":687},[329,858,859],{"class":339}," [\n",[329,861,863,866,869,871,874,877,880,883,886,889],{"class":331,"line":862},12,[329,864,865],{"class":360},"    'Cache full'",[329,867,868],{"class":687}," =>",[329,870,740],{"class":339},[329,872,873],{"class":360},"'cache_full'",[329,875,876],{"class":339},"] ",[329,878,879],{"class":687},"?",[329,881,882],{"class":360}," 'yes'",[329,884,885],{"class":687}," :",[329,887,888],{"class":360}," 'no'",[329,890,891],{"class":339},",\n",[329,893,895,898,900],{"class":331,"line":894},13,[329,896,897],{"class":360},"    'Cached files'",[329,899,868],{"class":687},[329,901,902],{"class":339}," $files,\n",[329,904,906,909,911,914,917],{"class":331,"line":905},14,[329,907,908],{"class":360},"    'Memory used'",[329,910,868],{"class":687},[329,912,913],{"class":339}," $toMegabytes($memory[",[329,915,916],{"class":360},"'used_memory'",[329,918,919],{"class":339},"]),\n",[329,921,923,926,928,930,933],{"class":331,"line":922},15,[329,924,925],{"class":360},"    'Memory free'",[329,927,868],{"class":687},[329,929,913],{"class":339},[329,931,932],{"class":360},"'free_memory'",[329,934,919],{"class":339},[329,936,938,941,943,946,948],{"class":331,"line":937},16,[329,939,940],{"class":360},"    'Interned strings free'",[329,942,868],{"class":687},[329,944,945],{"class":339}," $toMegabytes($strings[",[329,947,932],{"class":360},[329,949,919],{"class":339},[329,951,953,956,958,961,964],{"class":331,"line":952},17,[329,954,955],{"class":360},"    'Out of memory restarts'",[329,957,868],{"class":687},[329,959,960],{"class":339}," $stats[",[329,962,963],{"class":360},"'oom_restarts'",[329,965,966],{"class":339},"],\n",[329,968,970,973,975,977,980],{"class":331,"line":969},18,[329,971,972],{"class":360},"    'Hash restarts'",[329,974,868],{"class":687},[329,976,960],{"class":339},[329,978,979],{"class":360},"'hash_restarts'",[329,981,966],{"class":339},[329,983,985],{"class":331,"line":984},19,[329,986,746],{"class":339},[329,988,990],{"class":331,"line":989},20,[329,991,712],{"emptyLinePlaceholder":711},[329,993,995,998,1001,1004,1007,1010],{"class":331,"line":994},21,[329,996,997],{"class":687},"foreach",[329,999,1000],{"class":339}," ($report ",[329,1002,1003],{"class":687},"as",[329,1005,1006],{"class":339}," $label ",[329,1008,1009],{"class":687},"=>",[329,1011,1012],{"class":339}," $value) {\n",[329,1014,1016,1019,1022,1025,1027],{"class":331,"line":1015},22,[329,1017,1018],{"class":691},"    echo",[329,1020,1021],{"class":360}," \"",[329,1023,1024],{"class":339},"$label",[329,1026,357],{"class":360},[329,1028,1029],{"class":339},"$value\n",[329,1031,1033,1036],{"class":331,"line":1032},23,[329,1034,1035],{"class":360},"\"",[329,1037,819],{"class":339},[329,1039,1041],{"class":331,"line":1040},24,[329,1042,1043],{"class":339},"}\n",[1045,1046,1047],"caution",{},[238,1048,1049],{},"This file exposes cache and memory statistics. Remove it before deploying, or protect it.",[238,1051,1052,1053,1056,1057,1060],{},"Two of these numbers are available before you serve real traffic. Count the PHP files in your image with ",[269,1054,1055],{},"find /var/www/html -name '*.php' | wc -l"," for an upper bound on the file limit, and run your smoke tests or a ",[250,1058,1059],{"href":592},"preload script"," to fill the cache before you read the memory numbers. Then check:",[1062,1063,1064,1081,1092],"ul",{},[1065,1066,1067,1074,1075,1078,1079,272],"li",{},[1068,1069,1070,1071],"strong",{},"Cache full is ",[269,1072,1073],{},"yes"," or ",[1068,1076,1077],{},"out of memory restarts"," is climbing: raise ",[269,1080,455],{},[1065,1082,1083,1086,1087,1078,1090,272],{},[1068,1084,1085],{},"Cached files"," is close to the maximum or ",[1068,1088,1089],{},"hash restarts",[269,1091,485],{},[1065,1093,1094,1097,1098,272],{},[1068,1095,1096],{},"Interned strings free"," is near zero: raise ",[269,1099,470],{},[238,1101,1102],{},"Restarts are what you want to avoid. When OPcache runs out of room it restarts and clears the whole cache, so requests are slower until it fills back up.",[259,1104,1106],{"id":1105},"deploying","Deploying",[238,1108,1109,1110,660],{},"With OPcache enabled, files are cached until the container restarts, so every deployment should start a new container. The PHP manual is direct about this: with timestamps off, \"you must reset OPcache manually via opcache_reset(), opcache_invalidate() or by restarting the Web server for changes to the filesystem to take effect\" (",[250,1111,256],{"href":1112,"rel":1113,"target":255},"https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.validate-timestamps",[254],[238,1115,1116],{},"A few things that catch people out:",[1062,1118,1119,1129,1138],{},[1065,1120,1121,1124,1125,1128],{},[1068,1122,1123],{},"Running cache commands in a live container"," like ",[269,1126,1127],{},"docker exec php artisan optimize"," writes new files, but the running web workers keep serving the old cached copies. Restart the container instead.",[1065,1130,1131,1137],{},[1068,1132,1133,1136],{},[269,1134,1135],{},"opcache_reset()"," from the CLI does nothing for the web server."," The CLI has its own cache in its own process.",[1065,1139,1140,1143,1144,272],{},[1068,1141,1142],{},"Mounting code as a volume with OPcache enabled"," means your edits will not show up. Leave OPcache off in development, or set ",[269,1145,1146],{},"PHP_OPCACHE_VALIDATE_TIMESTAMPS=1",[383,1148,1149],{},[238,1150,1151,1152,1154,1155,1157],{},"If you need change detection with OPcache enabled, ",[269,1153,1146],{}," restores the Version 4 behavior with a check every ",[269,1156,447],{}," seconds.",[259,1159,1161],{"id":1160},"framework-notes","Framework notes",[1163,1164,91],"h4",{"id":1165},"laravel",[238,1167,1168,1169,1172,1173,1176,1177,1180,1181,1186],{},"Our ",[250,1170,1171],{"href":97},"Laravel automations"," run ",[269,1174,1175],{},"php artisan optimize"," in the entrypoint before the web server starts. That includes ",[269,1178,1179],{},"view:cache",", which \"precompiles all your Blade views so they are not compiled on demand\" (",[250,1182,1185],{"href":1183,"rel":1184,"target":255},"https://laravel.com/docs/12.x/deployment#optimizing-view-loading",[254],"Laravel docs","), so the cached config, routes, events, and views are on disk before OPcache sees them.",[238,1188,1189,1190,1193,1194,1198],{},"Queue workers, Horizon, the scheduler, Reverb, and Octane on Swoole or RoadRunner are CLI processes, so they follow ",[269,1191,1192],{},"PHP_OPCACHE_ENABLE_CLI",". Laravel's own advice applies to all of them: after a deploy, \"any long-running services such as queue workers, Laravel Reverb, or Laravel Octane should be reloaded / restarted to use the new code\" (",[250,1195,1185],{"href":1196,"rel":1197,"target":255},"https://laravel.com/docs/12.x/deployment#reloading-services",[254],"). A container restart does that.",[1163,1200,124],{"id":1201},"wordpress",[238,1203,1204,1205,1212,1213,1216,1217,1221,1222,272],{},"WordPress clears OPcache for the files it writes during core, plugin, and theme updates through the admin, using ",[250,1206,1209],{"href":1207,"rel":1208,"target":255},"https://developer.wordpress.org/reference/functions/wp_opcache_invalidate/",[254],[269,1210,1211],{},"wp_opcache_invalidate()",", so those updates keep working. Files changed outside of WordPress (",[269,1214,1215],{},"git pull",", WP-CLI, SFTP, or plugins that write PHP files directly) are not picked up until the container restarts. If you use the ",[250,1218,1220],{"href":1219},"/docs/framework-guides/wordpress/using-wordpress-with-docker#volume-based-approach-traditional","volume-based approach",", restart after those changes or set ",[269,1223,1146],{},[259,1225,593],{"id":1226},"preloading",[238,1228,1229,1230,1234,1235,1240,1241,1244],{},"Preloading compiles a set of files into OPcache when PHP starts and keeps them there \"until the server is shut down\" (",[250,1231,256],{"href":1232,"rel":1233,"target":255},"https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.preload",[254],"). Requests skip the autoloader for those classes. ",[250,1236,1239],{"href":1237,"rel":1238,"target":255},"https://symfony.com/doc/current/performance.html#use-the-opcache-class-preloading",[254],"Symfony generates a preload file for you"," at ",[269,1242,1243],{},"config/preload.php",". Laravel does not ship one.",[238,1246,1247,1248,1250],{},"Point ",[269,1249,581],{}," at your script:",[318,1252,1255],{"className":320,"code":1253,"filename":322,"highlights":1254,"language":325,"meta":10,"style":10},"services:\n  php:\n    image: my-app:latest\n    environment:\n      PHP_OPCACHE_ENABLE: \"1\"\n      PHP_OPCACHE_PRELOAD: \"/var/www/html/config/preload.php\"\n",[749],[269,1256,1257,1263,1269,1278,1284,1292],{"__ignoreMap":10},[329,1258,1259,1261],{"class":331,"line":332},[329,1260,336],{"class":335},[329,1262,340],{"class":339},[329,1264,1265,1267],{"class":331,"line":343},[329,1266,346],{"class":335},[329,1268,340],{"class":339},[329,1270,1271,1273,1275],{"class":331,"line":351},[329,1272,354],{"class":335},[329,1274,357],{"class":339},[329,1276,1277],{"class":360},"my-app:latest\n",[329,1279,1280,1282],{"class":331,"line":364},[329,1281,367],{"class":335},[329,1283,340],{"class":339},[329,1285,1286,1288,1290],{"class":331,"line":324},[329,1287,376],{"class":335},[329,1289,357],{"class":339},[329,1291,381],{"class":360},[329,1293,1295,1298,1300],{"class":1294,"line":749},[331,373],[329,1296,1297],{"class":335},"      PHP_OPCACHE_PRELOAD",[329,1299,357],{"class":339},[329,1301,1302],{"class":360},"\"/var/www/html/config/preload.php\"\n",[238,1304,1305],{},"Things to know before you turn it on:",[1062,1307,1308,1314,1326,1352],{},[1065,1309,1310,1313],{},[1068,1311,1312],{},"The file must exist."," PHP refuses to start if the preload script is missing or throws.",[1065,1315,1316,1321,1322,660],{},[1068,1317,1318,1319,272],{},"Preloading as root needs ",[269,1320,600],{}," Our images run as an unprivileged user, so this only matters if you run the container as root. The CLI is exempt on PHP 8.3 and newer (",[250,1323,256],{"href":1324,"rel":1325,"target":255},"https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.preload-user",[254],[1065,1327,1328,1331,1332,1334,1335,1337,1338,1340,1341,1343,1344,1347,1348,1351],{},[1068,1329,1330],{},"The CLI runs it too."," With ",[269,1333,394],{},", every ",[269,1336,680],{}," command preloads before it runs, so the script must work outside of a web request. Set ",[269,1339,398],{}," if you do not want that. Set ",[269,1342,581],{}," on your running service rather than as an ",[269,1345,1346],{},"ENV"," in your Dockerfile, so build steps like ",[269,1349,1350],{},"RUN composer install"," do not depend on it.",[1065,1353,1354,1357,1358,1360],{},[1068,1355,1356],{},"Preloaded files never change."," Even with ",[269,1359,1146],{},", preloaded code stays until restart.",[259,1362,560],{"id":1363},"jit",[238,1365,1366,1367,1371,1372,1377,1378,660],{},"The JIT compiler is off by default, as it is in PHP itself (",[250,1368,256],{"href":1369,"rel":1370,"target":255},"https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit",[254],"). The ",[250,1373,1376],{"href":1374,"rel":1375,"target":255},"https://wiki.php.net/rfc/jit",[254],"PHP JIT RFC"," found it \"doesn't seem to significantly improve real-life apps like WordPress\" while the benefit in \"non-Web, CPU-intensive scenarios\" is \"very substantial.\" Xdebug also turns it off: \"When Xdebug is loaded with PHP's JIT on, you will get a warning, and JIT will be disabled\" (",[250,1379,1382],{"href":1380,"rel":1381,"target":255},"https://xdebug.org/docs/compat",[254],"Xdebug docs",[238,1384,1385,1386,1388,1389,1392],{},"To enable it, set both variables. The buffer is added on top of ",[269,1387,455],{},", so ",[269,1390,1391],{},"64M"," with the default makes a 320 MB segment:",[318,1394,1397],{"className":320,"code":1395,"filename":322,"highlights":1396,"language":325,"meta":10,"style":10},"services:\n  php:\n    image: my-app:latest\n    environment:\n      PHP_OPCACHE_ENABLE: \"1\"\n      PHP_OPCACHE_JIT: \"tracing\"\n      PHP_OPCACHE_JIT_BUFFER_SIZE: \"64M\"\n",[749,764],[269,1398,1399,1405,1411,1419,1425,1433,1444],{"__ignoreMap":10},[329,1400,1401,1403],{"class":331,"line":332},[329,1402,336],{"class":335},[329,1404,340],{"class":339},[329,1406,1407,1409],{"class":331,"line":343},[329,1408,346],{"class":335},[329,1410,340],{"class":339},[329,1412,1413,1415,1417],{"class":331,"line":351},[329,1414,354],{"class":335},[329,1416,357],{"class":339},[329,1418,1277],{"class":360},[329,1420,1421,1423],{"class":331,"line":364},[329,1422,367],{"class":335},[329,1424,340],{"class":339},[329,1426,1427,1429,1431],{"class":331,"line":324},[329,1428,376],{"class":335},[329,1430,357],{"class":339},[329,1432,381],{"class":360},[329,1434,1436,1439,1441],{"class":1435,"line":749},[331,373],[329,1437,1438],{"class":335},"      PHP_OPCACHE_JIT",[329,1440,357],{"class":339},[329,1442,1443],{"class":360},"\"tracing\"\n",[329,1445,1447,1450,1452],{"class":1446,"line":764},[331,373],[329,1448,1449],{"class":335},"      PHP_OPCACHE_JIT_BUFFER_SIZE",[329,1451,357],{"class":339},[329,1453,1454],{"class":360},"\"64M\"\n",[238,1456,1457],{},"Measure before and after. If your response times do not move, leave it off.",[259,1459,1461],{"id":1460},"advanced-settings","Advanced settings",[238,1463,1464,1465,1468],{},"If there are options that you don't see in the environment variables, you can set them through a ",[250,1466,1467],{"href":193},"custom ini file",". For example, in some ocasions you may want to set:",[1062,1470,1471,1481],{},[1065,1472,1473,1476,1477,660],{},[269,1474,1475],{},"opcache.huge_code_pages=1"," copies compiled code into huge pages. It \"requires appropriate OS configuration\" (",[250,1478,256],{"href":1479,"rel":1480,"target":255},"https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.huge_code_pages",[254],[1065,1482,1483,1486,1487,660],{},[269,1484,1485],{},"opcache.file_cache=/path"," adds a second-level cache on disk that helps \"at server restart or SHM reset\" (",[250,1488,256],{"href":1489,"rel":1490,"target":255},"https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.file-cache",[254],[242,1492,1494],{"id":1493},"php-realpath-cache","PHP realpath cache",[238,1496,1497,1498,1501,1502,790,1505,1510,1511,1501,1514,1517,1518,1521,1522,1525],{},"PHP caches the resolved path of every file it opens so it does not repeat the lookup on each request. Symfony's guide pairs this cache with OPcache and says applications that open many PHP files \"should use at least\" ",[269,1499,1500],{},"realpath_cache_size=4096K"," and ",[269,1503,1504],{},"realpath_cache_ttl=600",[250,1506,1509],{"href":1507,"rel":1508,"target":255},"https://symfony.com/doc/current/performance.html#configure-the-php-realpath-cache",[254],"Symfony docs","). Our images ship both as ",[269,1512,1513],{},"PHP_REALPATH_CACHE_SIZE",[269,1515,1516],{},"PHP_REALPATH_CACHE_TTL",". PHP disables this cache when ",[269,1519,1520],{},"open_basedir"," is set, so leave ",[269,1523,1524],{},"PHP_OPEN_BASEDIR"," empty in production unless you need it.",[1527,1528,1529],"tip",{"to":223},[238,1530,1531],{},"The full list of variables and their defaults is in the environment variable specification.",[1533,1534,1535],"style",{},"html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}",{"title":10,"searchDepth":343,"depth":343,"links":1537},[1538,1549],{"id":244,"depth":343,"text":245,"children":1539},[1540,1541,1542,1543,1544,1545,1546,1547,1548],{"id":261,"depth":351,"text":262},{"id":402,"depth":351,"text":403},{"id":645,"depth":351,"text":646},{"id":666,"depth":351,"text":667},{"id":1105,"depth":351,"text":1106},{"id":1160,"depth":351,"text":1161},{"id":1226,"depth":351,"text":593},{"id":1363,"depth":351,"text":560},{"id":1460,"depth":351,"text":1461},{"id":1493,"depth":343,"text":1494},"The settings that matter for PHP performance in production, where our defaults come from, and how to measure and tune them for your application.","md",null,{"head":1554,"layout":7},{"title":1555},"Production Performance Tuning - Docker PHP - Server Side Up",{"title":183,"description":1550},"gqY9FEa_jACBrbseN3sHj-oZrLKE44xm18Iw6QTpPSI",[1559,1561],{"title":179,"path":180,"stem":181,"description":1560,"children":-1},"Step-by-step guides for migrating between major versions of the serversideup/php Docker images.",{"title":192,"path":193,"stem":194,"description":1562,"children":-1},"Learn how to change common PHP settings with environment variables or your own php.ini file.",1789148867775]