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