[{"data":1,"prerenderedAt":1577},["ShallowReactive",2],{"navigation":3,"/docs/upgrading/upgrade-to-v5":249,"/docs/upgrading/upgrade-to-v5-surround":1572},[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":195,"body":251,"description":1563,"extension":1564,"links":1565,"meta":1566,"navigation":1569,"path":196,"redirect":1565,"seo":1570,"stem":197,"__hash__":1571},"docs/docs/6.upgrading/2.upgrade-to-v5.md",{"type":252,"value":253,"toc":1540},"minimark",[254,266,275,280,283,329,333,547,551,574,580,601,611,614,645,651,675,692,695,716,736,739,757,783,786,801,811,814,830,844,849,874,901,918,923,946,953,965,990,996,1001,1010,1013,1040,1045,1060,1063,1078,1237,1251,1255,1428,1434,1438,1536],[255,256,257],"lead-p",{},[258,259,260,261,265],"p",{},"Version 5 tunes OPcache for production and follows Caddy's logging defaults on FrankenPHP. ",[262,263,264],"code",{},"TRUSTED_PROXY"," now works the same on every web server. Most apps only need to check the first two changes below.",[258,267,268,269,274],{},"PHP 7.4 and 8.0 are no longer built. See ",[270,271,273],"a",{"href":272},"/docs/upgrading/upgrade-guide#php-and-os-versions-we-no-longer-build","PHP and OS versions we no longer build",".",[276,277,279],"h2",{"id":278},"stay-on-v4-while-you-review","Stay on v4 while you review",[258,281,282],{},"Pin your tag to the last v4 release. Version-pinned tags are never rebuilt. You get no security updates until you move to v5.",[284,285,292],"pre",{"className":286,"code":287,"filename":288,"highlights":289,"language":291,"meta":10,"style":10},"language-yaml shiki shiki-themes github-dark","services:\n  php:\n    image: serversideup/php:8.5-fpm-nginx-v4.5.3\n","compose.yml",[290],3,"yaml",[262,293,294,307,315],{"__ignoreMap":10},[295,296,299,303],"span",{"class":297,"line":298},"line",1,[295,300,302],{"class":301},"s4JwU","services",[295,304,306],{"class":305},"s95oV",":\n",[295,308,310,313],{"class":297,"line":309},2,[295,311,312],{"class":301},"  php",[295,314,306],{"class":305},[295,316,319,322,325],{"class":317,"line":290},[297,318],"highlight",[295,320,321],{"class":301},"    image",[295,323,324],{"class":305},": ",[295,326,328],{"class":327},"sU2Wk","serversideup/php:8.5-fpm-nginx-v4.5.3\n",[276,330,332],{"id":331},"breaking-changes","Breaking changes",[334,335,336,352],"table",{},[337,338,339],"thead",{},[340,341,342,346,349],"tr",{},[343,344,345],"th",{},"Change",[343,347,348],{},"Likelihood of impact",[343,350,351],{},"You're affected if",[353,354,355,374,392,407,422,436,452,472,489,509,525],"tbody",{},[340,356,357,364,367],{},[358,359,360],"td",{},[270,361,363],{"href":362},"#opcache-no-longer-checks-for-file-changes","OPcache no longer checks for file changes",[358,365,366],{},"High",[358,368,369,370,373],{},"You set ",[262,371,372],{},"PHP_OPCACHE_ENABLE=1"," and change code without restarting the container",[340,375,376,382,385],{},[358,377,378],{},[270,379,381],{"href":380},"#frankenphp-logs-go-to-stderr-as-json","FrankenPHP logs go to stderr as JSON",[358,383,384],{},"Medium",[358,386,387,388,391],{},"You run ",[262,389,390],{},"frankenphp"," and read or filter its logs",[340,393,394,400,402],{},[358,395,396],{},[270,397,399],{"href":398},"#octane-uses-the-images-caddyfile","Octane uses the image's Caddyfile",[358,401,384],{},[358,403,404,405],{},"You run Laravel Octane on ",[262,406,390],{},[340,408,409,415,417],{},[358,410,411],{},[270,412,414],{"href":413},"#mercure-10-rejects-the-old-jwt-options","Mercure 1.0 rejects the old JWT options",[358,416,384],{},[358,418,419,420],{},"You run the Mercure hub on ",[262,421,390],{},[340,423,424,430,433],{},[358,425,426],{},[270,427,429],{"href":428},"#session-cookies-are-httponly","Session cookies are HttpOnly",[358,431,432],{},"Low",[358,434,435],{},"Your JavaScript reads PHP's native session cookie",[340,437,438,444,446],{},[358,439,440],{},[270,441,443],{"href":442},"#session-upload-progress-is-off","Session upload progress is off",[358,445,432],{},[358,447,448,449],{},"Your app reads upload progress from ",[262,450,451],{},"$_SESSION",[340,453,454,463,465],{},[358,455,456],{},[270,457,459,460],{"href":458},"#s6-dependencies-use-dependenciesd","S6 dependencies use ",[262,461,462],{},"dependencies.d/",[358,464,432],{},[358,466,467,468,471],{},"Your Dockerfile appends to an S6 ",[262,469,470],{},"dependencies"," file",[340,473,474,484,486],{},[358,475,476],{},[270,477,479,480,483],{"href":478},"#the-s6-user-bundle-moved","The S6 ",[262,481,482],{},"user"," bundle moved",[358,485,432],{},[358,487,488],{},"Your Dockerfile registers your own S6 service",[340,490,491,501,503],{},[358,492,493],{},[270,494,496,497,500],{"href":495},"#frankenphp-sets-remote_addr-to-the-client-ip","FrankenPHP sets ",[262,498,499],{},"REMOTE_ADDR"," to the client IP",[358,502,432],{},[358,504,505,506,508],{},"Your app compares ",[262,507,499],{}," to a proxy address",[340,510,511,517,519],{},[358,512,513],{},[270,514,516],{"href":515},"#caddy-limits-request-headers","Caddy limits request headers",[358,518,432],{},[358,520,521,522,524],{},"Requests to ",[262,523,390],{}," carry more than 16 KiB of headers",[340,526,527,536,538],{},[358,528,529],{},[270,530,532,535],{"href":531},"#num_threads-no-longer-includes-workers",[262,533,534],{},"num_threads"," no longer includes workers",[358,537,432],{},[358,539,369,540,542,543,546],{},[262,541,534],{}," in ",[262,544,545],{},"FRANKENPHP_CONFIG"," with workers",[548,549,363],"h3",{"id":550},"opcache-no-longer-checks-for-file-changes",[258,552,553,556,557,560,561,568,569,573],{},[262,554,555],{},"PHP_OPCACHE_VALIDATE_TIMESTAMPS"," now defaults to ",[262,558,559],{},"0",". With OPcache on, PHP caches each file until the container restarts, which is right for code built into the image. Version 4 checked every file every two seconds and used PHP's stock memory sizes. Version 5 uses the values from ",[270,562,567],{"href":563,"rel":564,"target":566},"https://symfony.com/doc/current/performance.html#configure-opcache-for-maximum-performance",[565],"nofollow","_blank","Symfony's performance guide"," instead (see ",[270,570,572],{"href":571},"#changed-defaults","Changed defaults",").",[258,575,576,577,579],{},"You're affected if you set ",[262,578,372],{}," and any of these apply:",[581,582,583,587,594],"ul",{},[584,585,586],"li",{},"Your code is mounted as a volume and you edit it in place.",[584,588,589,590,593],{},"You run WordPress from a volume and update it with ",[262,591,592],{},"git pull",", WP-CLI, or SFTP.",[584,595,596,597,600],{},"You run commands like ",[262,598,599],{},"docker exec ... php artisan optimize"," and expect the web server to pick up the new files.",[258,602,603,604,607,608,610],{},"Restart the container after code changes (recommended). To get the v4 behavior instead, set ",[262,605,606],{},"PHP_OPCACHE_VALIDATE_TIMESTAMPS=1",". ",[270,609,153],{"href":154}," covers the OPcache settings.",[548,612,381],{"id":613},"frankenphp-logs-go-to-stderr-as-json",[258,615,616,556,619,622,623,626,627,630,631,633,634,636,637,640,641,644],{},[262,617,618],{},"CADDY_LOG_OUTPUT",[262,620,621],{},"stderr"," and ",[262,624,625],{},"CADDY_LOG_FORMAT"," to ",[262,628,629],{},"auto",". Caddy, the official FrankenPHP image, and Laravel Octane all write logs to ",[262,632,621],{},". With ",[262,635,629],{},", Caddy writes JSON when there is no terminal, which is always the case in a container. Version 4 forced ",[262,638,639],{},"console"," lines on ",[262,642,643],{},"stdout",", which put ANSI color codes into log pipelines.",[258,646,647,650],{},[262,648,649],{},"docker logs",", Docker Compose, and Kubernetes show both streams, so most setups only notice the format. You're affected if:",[581,652,653,663,669],{},[584,654,655,656,658,659,662],{},"You only capture ",[262,657,643],{},", for example with ",[262,660,661],{},"2>/dev/null"," or a log driver rule.",[584,664,665,666,668],{},"Your log pipeline parses the ",[262,667,639],{}," lines.",[584,670,671,672,274],{},"You prefer reading colored lines with ",[262,673,674],{},"docker compose logs",[258,676,677,678,681,682,622,685,688,689,691],{},"Let your pipeline read the ",[262,679,680],{},"level"," field of each JSON entry (recommended). To restore v4, set ",[262,683,684],{},"CADDY_LOG_FORMAT=console",[262,686,687],{},"CADDY_LOG_OUTPUT=stdout",". Don't use either override with Octane, which only relays ",[262,690,621],{}," and only parses JSON.",[548,693,399],{"id":694},"octane-uses-the-images-caddyfile",[258,696,697,698,626,701,704,705,708,709,711,712,715],{},"Pass ",[262,699,700],{},"--caddyfile=/etc/frankenphp/Caddyfile",[262,702,703],{},"octane:start",". The Caddyfile now adds the worker and routing itself when Octane starts FrankenPHP. Remove any ",[262,706,707],{},"worker { }"," block from ",[262,710,545],{}," and the routing you added to ",[262,713,714],{},"CADDY_PHP_SERVER_OPTIONS",". Keeping the worker block fails with \"global workers must not have duplicate filenames\".",[258,717,718,719,722,723,622,726,728,729,732,733,274],{},"Octane needs the Caddy admin API and sets ",[262,720,721],{},"CADDY_GLOBAL_OPTIONS"," itself. While Octane runs, ",[262,724,725],{},"CADDY_ADMIN",[262,727,721],{}," are ignored. Put global options in a file in ",[262,730,731],{},"/etc/frankenphp/caddyfile-global.d/"," instead. Classic mode doesn't change. See ",[270,734,735],{"href":109},"Laravel Octane",[548,737,414],{"id":738},"mercure-10-rejects-the-old-jwt-options",[258,740,741,742,745,746,749,750,753,754,274],{},"Version 5 ships FrankenPHP 1.13, which includes Mercure 1.0. FrankenPHP fails to start if you configure the hub with ",[262,743,744],{},"publisher_jwt"," or ",[262,747,748],{},"subscriber_jwt",", including through the ",[262,751,752],{},"mercure"," array in ",[262,755,756],{},"config/octane.php",[258,758,759,760,763,764,767,768,626,771,774,775,777,778,274],{},"Remove those options and turn the hub on with ",[262,761,762],{},"MERCURE_ENABLED=true"," and the other ",[262,765,766],{},"MERCURE_*"," variables, which work in classic mode and with Octane. If your app still signs 0.x tokens, add ",[262,769,770],{},"protocol_version_compatibility 8",[262,772,773],{},"MERCURE_EXTRA_DIRECTIVES"," while you migrate. See ",[270,776,70],{"href":71}," and the ",[270,779,782],{"href":780,"rel":781,"target":566},"https://github.com/dunglas/mercure/blob/v1.0.3/docs/UPGRADE.md",[565],"Mercure 1.0 upgrade guide",[548,784,429],{"id":785},"session-cookies-are-httponly",[258,787,788,556,791,794,795,800],{},[262,789,790],{},"PHP_SESSION_COOKIE_HTTPONLY",[262,792,793],{},"On",", as ",[270,796,799],{"href":797,"rel":798,"target":566},"https://www.php.net/manual/en/session.security.ini.php",[565],"PHP recommends",", so browser scripts can't read PHP's native session cookie. Laravel, Symfony, and WordPress set their own session cookies and aren't affected.",[258,802,803,804,807,808,274],{},"If your app calls ",[262,805,806],{},"session_start()"," and reads the session cookie from JavaScript, set ",[262,809,810],{},"PHP_SESSION_COOKIE_HTTPONLY=Off",[548,812,443],{"id":813},"session-upload-progress-is-off",[258,815,816,817,820,821,823,824,829],{},"Since v5.0.3, ",[262,818,819],{},"PHP_SESSION_UPLOAD_PROGRESS_ENABLED"," defaults to ",[262,822,559],{},", following ",[270,825,828],{"href":826,"rel":827,"target":566},"https://www.php.net/archive/2026.php#2026-10-23-1",[565],"PHP's October 23, 2026 security advisory",". PHP's own default is on.",[258,831,832,833,835,836,839,840,843],{},"If your app reads upload progress from ",[262,834,451],{},", set it to ",[262,837,838],{},"1",", and only if your app never changes ",[262,841,842],{},"session.serialize_handler"," at runtime.",[548,845,459,847],{"id":846},"s6-dependencies-use-dependenciesd",[262,848,462],{},[258,850,851,622,854,857,858,860,861,866,867,869,870,873],{},[262,852,853],{},"fpm-nginx",[262,855,856],{},"fpm-apache"," now declare service dependencies with the ",[262,859,462],{}," directory that ",[270,862,865],{"href":863,"rel":864,"target":566},"https://github.com/just-containers/s6-overlay#writing-a-service-script",[565],"S6 Overlay documents",", instead of the deprecated ",[262,868,470],{}," file. This fixed a race where ",[262,871,872],{},"php-fpm"," could start before its pool user was written when the container runs as root.",[258,875,876,877,879,880,882,883,886,887,889,890,893,894,897,898,900],{},"S6 ignores the ",[262,878,470],{}," file once ",[262,881,462],{}," exists. If your Dockerfile appends to ",[262,884,885],{},"/etc/s6-overlay/s6-rc.d/\u003Cservice>/dependencies"," for ",[262,888,872],{},", ",[262,891,892],{},"nginx",", or ",[262,895,896],{},"apache2",", create an empty file in ",[262,899,462],{}," instead:",[284,902,907],{"className":903,"code":904,"filename":905,"language":906,"meta":10,"style":10},"language-dockerfile shiki shiki-themes github-dark","RUN touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/my-service\n","Dockerfile","dockerfile",[262,908,909],{"__ignoreMap":10},[295,910,911,915],{"class":297,"line":298},[295,912,914],{"class":913},"snl16","RUN",[295,916,917],{"class":305}," touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/my-service\n",[548,919,479,921,483],{"id":920},"the-s6-user-bundle-moved",[262,922,482],{},[258,924,925,926,931,932,934,935,938,939,607,942,945],{},"Version 5 ships ",[270,927,930],{"href":928,"rel":929,"target":566},"https://github.com/just-containers/s6-overlay/blob/v3.2.3.2/CHANGELOG.md",[565],"S6 Overlay v3.2.3.2",", which defines the ",[262,933,482],{}," bundle in ",[262,936,937],{},"/etc/s6-overlay/user-bundles.d/"," instead of ",[262,940,941],{},"/etc/s6-overlay/s6-rc.d/",[262,943,944],{},"docker-php-serversideup-s6-init"," already writes to the new location.",[258,947,948,949,952],{},"If your Dockerfile creates ",[262,950,951],{},"/etc/s6-overlay/s6-rc.d/user/contents.d/\u003Cservice>",", create the empty file in the new directory instead:",[284,954,956],{"className":903,"code":955,"filename":905,"language":906,"meta":10,"style":10},"RUN touch /etc/s6-overlay/user-bundles.d/user/contents.d/my-service\n",[262,957,958],{"__ignoreMap":10},[295,959,960,962],{"class":297,"line":298},[295,961,914],{"class":913},[295,963,964],{"class":305}," touch /etc/s6-overlay/user-bundles.d/user/contents.d/my-service\n",[966,967,968],"warning",{},[258,969,970,971,974,975,978,979,981,982,985,986,989],{},"Don't recreate ",[262,972,973],{},"/etc/s6-overlay/s6-rc.d/user",". When it exists, S6 Overlay ignores ",[262,976,977],{},"user-bundles.d/",", so ",[262,980,872],{}," and the web server never start. As ",[262,983,984],{},"www-data",", the container doesn't boot at all, because S6 Overlay tries to write to ",[262,987,988],{},"/etc/s6-overlay"," at startup.",[258,991,992,993,995],{},"See ",[270,994,178],{"href":179}," for writing your own services.",[548,997,496,999,500],{"id":998},"frankenphp-sets-remote_addr-to-the-client-ip",[262,1000,499],{},[258,1002,1003,1004,1007,1008,274],{},"Version 4 set ",[262,1005,1006],{},"$_SERVER['REMOTE_ADDR']"," to the TCP peer on FrankenPHP. It's now the client IP that Caddy resolves from trusted proxies, the same as NGINX and Apache. Caddy runs in strict mode, so a client behind a trusted proxy can't forge it. See ",[270,1009,149],{"href":150},[548,1011,516],{"id":1012},"caddy-limits-request-headers",[258,1014,1015,1016,1021,1022,1025,1026,1028,1029,1032,1033,1036,1037,274],{},"FrankenPHP 1.13 includes ",[270,1017,1020],{"href":1018,"rel":1019,"target":566},"https://github.com/caddyserver/caddy/releases/tag/v2.11.7",[565],"Caddy 2.11.7",". Requests with more than 16 KiB of headers now get ",[262,1023,1024],{},"431 Request Header Fields Too Large",", where v4 allowed 1 MB. Large cookies are the usual cause. Headers with a ",[262,1027,274],{}," in their name are now dropped, like headers with a ",[262,1030,1031],{},"_",", because PHP reads both as ",[262,1034,1035],{},"-"," and a client could use them to spoof headers such as ",[262,1038,1039],{},"X-Forwarded-For",[548,1041,1043,535],{"id":1042},"num_threads-no-longer-includes-workers",[262,1044,534],{},[258,1046,1047,1048,542,1050,1052,1053,1056,1057,1059],{},"If you set ",[262,1049,534],{},[262,1051,545],{}," while running workers, including Octane, FrankenPHP now starts that many threads on top of the worker threads. It fails to start if ",[262,1054,1055],{},"max_threads"," is lower than the total. Lower ",[262,1058,534],{}," to the number of threads you want for regular requests.",[276,1061,572],{"id":1062},"changed-defaults",[258,1064,1065,1066,1068,1069,1072,1073,274],{},"The OPcache values only apply when ",[262,1067,372],{},". OPcache only uses the memory as it caches files. ",[262,1070,1071],{},"PHP_REALPATH_CACHE_TTL"," applies with OPcache on or off and comes from the same ",[270,1074,1077],{"href":1075,"rel":1076,"target":566},"https://symfony.com/doc/current/performance.html#configure-the-php-realpath-cache",[565],"Symfony recommendation",[334,1079,1080,1093],{},[337,1081,1082],{},[340,1083,1084,1087,1090],{},[343,1085,1086],{},"Variable",[343,1088,1089],{},"v4",[343,1091,1092],{},"v5",[353,1094,1095,1109,1126,1143,1160,1176,1189,1203,1221],{},[340,1096,1097,1101,1105],{},[358,1098,1099],{},[262,1100,555],{},[358,1102,1103],{},[262,1104,838],{},[358,1106,1107],{},[262,1108,559],{},[340,1110,1111,1116,1121],{},[358,1112,1113],{},[262,1114,1115],{},"PHP_OPCACHE_MEMORY_CONSUMPTION",[358,1117,1118],{},[262,1119,1120],{},"128",[358,1122,1123],{},[262,1124,1125],{},"256",[340,1127,1128,1133,1138],{},[358,1129,1130],{},[262,1131,1132],{},"PHP_OPCACHE_INTERNED_STRINGS_BUFFER",[358,1134,1135],{},[262,1136,1137],{},"8",[358,1139,1140],{},[262,1141,1142],{},"32",[340,1144,1145,1150,1155],{},[358,1146,1147],{},[262,1148,1149],{},"PHP_OPCACHE_MAX_ACCELERATED_FILES",[358,1151,1152],{},[262,1153,1154],{},"10000",[358,1156,1157],{},[262,1158,1159],{},"32531",[340,1161,1162,1166,1171],{},[358,1163,1164],{},[262,1165,1071],{},[358,1167,1168],{},[262,1169,1170],{},"120",[358,1172,1173],{},[262,1174,1175],{},"600",[340,1177,1178,1182,1185],{},[358,1179,1180],{},[262,1181,790],{},[358,1183,1184],{},"PHP default (off)",[358,1186,1187],{},[262,1188,793],{},[340,1190,1191,1195,1198],{},[358,1192,1193],{},[262,1194,819],{},[358,1196,1197],{},"PHP default (on)",[358,1199,1200,1202],{},[262,1201,559],{}," (since v5.0.3)",[340,1204,1205,1213,1217],{},[358,1206,1207,1209,1210,1212],{},[262,1208,618],{}," (",[262,1211,390],{},")",[358,1214,1215],{},[262,1216,643],{},[358,1218,1219],{},[262,1220,621],{},[340,1222,1223,1229,1233],{},[358,1224,1225,1209,1227,1212],{},[262,1226,625],{},[262,1228,390],{},[358,1230,1231],{},[262,1232,639],{},[358,1234,1235],{},[262,1236,629],{},[258,1238,1239,1242,1243,1246,1247,1250],{},[262,1240,1241],{},"PHP_OPCACHE_FORCE_RESTART_TIMEOUT"," existed in v4 but never reached ",[262,1244,1245],{},"php.ini",". It works now. Its default of ",[262,1248,1249],{},"180"," matches PHP's. Nothing changes unless you set it.",[276,1252,1254],{"id":1253},"new-variables","New variables",[334,1256,1257,1266],{},[337,1258,1259],{},[340,1260,1261,1263],{},[343,1262,1086],{},[343,1264,1265],{},"What it does",[353,1267,1268,1281,1299,1309,1319,1332,1342,1352,1363,1379,1395,1404,1418],{},[340,1269,1270,1275],{},[358,1271,1272],{},[262,1273,1274],{},"PHP_OPCACHE_ENABLE_CLI",[358,1276,1277,1278,1280],{},"Whether CLI commands use OPcache when it's on. Defaults to ",[262,1279,838],{},", as v4 did.",[340,1282,1283,1288],{},[358,1284,1285],{},[262,1286,1287],{},"PHP_OPCACHE_PRELOAD",[358,1289,1290,1291,1294,1295,1298],{},"Path to a preload script. Set it on the running service, not as an ",[262,1292,1293],{},"ENV"," in your Dockerfile, so build steps such as ",[262,1296,1297],{},"composer install"," don't depend on it.",[340,1300,1301,1306],{},[358,1302,1303],{},[262,1304,1305],{},"PHP_OPCACHE_PRELOAD_USER",[358,1307,1308],{},"The user to preload as when the container runs as root.",[340,1310,1311,1316],{},[358,1312,1313],{},[262,1314,1315],{},"PHP_DISABLE_FUNCTIONS",[358,1317,1318],{},"Functions to disable. Empty by default.",[340,1320,1321,1329],{},[358,1322,1323,889,1326],{},[262,1324,1325],{},"PHP_FILE_UPLOADS",[262,1327,1328],{},"PHP_MAX_FILE_UPLOADS",[358,1330,1331],{},"Turn file uploads off, or cap the files per request.",[340,1333,1334,1339],{},[358,1335,1336],{},[262,1337,1338],{},"PHP_HTML_ERRORS",[358,1340,1341],{},"Format on-screen errors as HTML.",[340,1343,1344,1349],{},[358,1345,1346],{},[262,1347,1348],{},"PHP_REALPATH_CACHE_SIZE",[358,1350,1351],{},"Size of PHP's realpath cache.",[340,1353,1354,1360],{},[358,1355,1356,889,1358],{},[262,1357,790],{},[262,1359,819],{},[358,1361,1362],{},"See the breaking changes above.",[340,1364,1365,1369],{},[358,1366,1367],{},[262,1368,264],{},[358,1370,1371,1372,889,1374,1376,1377,274],{},"Which proxies to trust for the client IP on ",[262,1373,853],{},[262,1375,856],{},", and ",[262,1378,390],{},[340,1380,1381,1386],{},[358,1382,1383],{},[262,1384,1385],{},"CADDY_ACME_PROFILE",[358,1387,1388,1389,1391,1392,274],{},"Request a Let's Encrypt certificate profile on ",[262,1390,390],{},", such as ",[262,1393,1394],{},"shortlived",[340,1396,1397,1401],{},[358,1398,1399],{},[262,1400,766],{},[358,1402,1403],{},"Run FrankenPHP's Mercure hub.",[340,1405,1406,1411],{},[358,1407,1408],{},[262,1409,1410],{},"AUTORUN_LARAVEL_SKIP_IF_NOT_FOUND",[358,1412,1413,1414,1417],{},"Start the container when Laravel isn't in ",[262,1415,1416],{},"APP_BASE_DIR"," yet.",[340,1419,1420,1425],{},[358,1421,1422],{},[262,1423,1424],{},"LARAVEL_OCTANE",[358,1426,1427],{},"Set by Octane, not by you.",[258,1429,1430,1431,274],{},"Defaults and accepted values are in the ",[270,1432,1433],{"href":243},"environment variable reference",[276,1435,1437],{"id":1436},"new-features","New features",[581,1439,1440,1449,1467,1478,1492,1500,1513,1523],{},[584,1441,1442,1446,1447,274],{},[1443,1444,1445],"strong",{},"Octane with the image's Caddyfile."," Octane gets the same trusted proxies, security headers, asset caching, SSL modes, and health check as classic mode. See ",[270,1448,735],{"href":109},[584,1450,1451,1454,1455,1457,1458,889,1460,1376,1462,1464,1465,274],{},[1443,1452,1453],{},"Trusted proxies on every web server."," ",[262,1456,264],{}," behaves the same on ",[262,1459,853],{},[262,1461,856],{},[262,1463,390],{},", through more than one Docker hop. See ",[270,1466,149],{"href":150},[584,1468,1469,1472,1473,1475,1476,274],{},[1443,1470,1471],{},"Short-lived and IP-address certificates"," on FrankenPHP with ",[262,1474,1385],{},". See ",[270,1477,142],{"href":143},[584,1479,1480,1485,1486,1475,1489,274],{},[1443,1481,1482],{},[262,1483,1484],{},"healthcheck-nightwatch"," runs ",[262,1487,1488],{},"php artisan nightwatch:status",[270,1490,1491],{"href":97},"Laravel health checks",[584,1493,1494,1497,1498,274],{},[1443,1495,1496],{},"Mercure from environment variables",", in classic mode or with Octane. See ",[270,1499,70],{"href":71},[584,1501,1502,1505,1506,542,1509,1512],{},[1443,1503,1504],{},"Caddyfile rules from a folder."," Put a ",[262,1507,1508],{},".caddyfile",[262,1510,1511],{},"/etc/frankenphp/caddyfile-server.d/"," to add headers, redirects, or matchers to your site.",[584,1514,1515,1518,1519,1522],{},[1443,1516,1517],{},"Redacted Mercure tokens."," FrankenPHP request logs replace the ",[262,1520,1521],{},"authorization"," query parameter in every log format.",[584,1524,1525,1528,1529,622,1532,1535],{},[1443,1526,1527],{},"Tested before publishing."," Every image is started and checked on ",[262,1530,1531],{},"amd64",[262,1533,1534],{},"arm64"," before it reaches Docker Hub. If one image fails, nothing from that build is published.",[1537,1538,1539],"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}",{"title":10,"searchDepth":309,"depth":309,"links":1541},[1542,1543,1560,1561,1562],{"id":278,"depth":309,"text":279},{"id":331,"depth":309,"text":332,"children":1544},[1545,1546,1547,1548,1549,1550,1551,1553,1555,1557,1558],{"id":550,"depth":290,"text":363},{"id":613,"depth":290,"text":381},{"id":694,"depth":290,"text":399},{"id":738,"depth":290,"text":414},{"id":785,"depth":290,"text":429},{"id":813,"depth":290,"text":443},{"id":846,"depth":290,"text":1552},"S6 dependencies use dependencies.d/",{"id":920,"depth":290,"text":1554},"The S6 user bundle moved",{"id":998,"depth":290,"text":1556},"FrankenPHP sets REMOTE_ADDR to the client IP",{"id":1012,"depth":290,"text":516},{"id":1042,"depth":290,"text":1559},"num_threads no longer includes workers",{"id":1062,"depth":309,"text":572},{"id":1253,"depth":309,"text":1254},{"id":1436,"depth":309,"text":1437},"The breaking changes, new defaults, and new variables when you move from serversideup/php v4 to v5.","md",null,{"head":1567,"layout":7},{"title":1568},"Upgrade to v5 - Docker PHP - Server Side Up",true,{"title":195,"description":1563},"PGPyLeQ0TR7P22A-7zzPc7WAm4ToDqL__cHCpQ7QHdA",[1573,1575],{"title":191,"path":192,"stem":193,"description":1574,"children":-1},"How serversideup/php releases and tags work, which tag to use, and how to patch an image yourself.",{"title":199,"path":200,"stem":201,"description":1576,"children":-1},"What changed when serversideup/php moved from v3 to v4, and the defaults to check before you update your tag.",1793811963603]