[{"data":1,"prerenderedAt":3080},["ShallowReactive",2],{"navigation":3,"/docs/guide/major-version-migrations":229,"/docs/guide/major-version-migrations-surround":3075},[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":179,"body":231,"description":3067,"extension":3068,"links":3069,"meta":3070,"navigation":434,"path":180,"redirect":3069,"seo":3073,"stem":181,"__hash__":3074},"docs/docs/5.guide/5.major-version-migrations.md",{"type":232,"value":233,"toc":3042},"minimark",[234,250,255,265,309,316,331,343,348,370,400,407,562,577,584,588,602,612,619,657,661,680,684,721,725,731,742,745,756,782,789,804,820,823,851,868,877,889,902,909,935,958,970,976,987,1017,1024,1036,1058,1069,1083,1103,1111,1124,1134,1172,1176,1203,1210,1226,1230,1246,1250,1265,1364,1374,1416,1420,1590,1595,1599,1724,1728,1732,1810,1812,1853,1857,1864,1868,1871,1970,1974,2024,2028,2031,2035,2038,2064,2067,2071,2076,2127,2132,2180,2183,2187,2194,2211,2266,2318,2325,2329,2332,2410,2414,2419,2423,2426,2433,2443,2448,2455,2482,2486,2501,2507,2518,2529,2539,2542,2551,2557,2561,2564,2568,2618,2622,2625,2629,2634,2637,2661,2664,2697,2701,2721,2724,2997,3001,3026,3030,3038],[235,236,237],"lead-p",{},[238,239,240,241,245,246,249],"p",{},"When we ship a new major version of ",[242,243,244],"code",{},"serversideup/php",", we collect the breaking changes, new features, and migration checklists in this guide. Use it whenever you're crossing a major version boundary — for example, V3 → V4 or V2 → V3. For day-to-day patches and security updates, see the ",[247,248,40],"a",{"href":41}," instead.",[251,252,254],"h2",{"id":253},"dropped-php-versions","Dropped PHP versions",[238,256,257,258,264],{},"We keep building an end-of-life PHP version while its official base image exists and the operating system underneath still receives security updates. Once the operating system is end of life too, a rebuild cannot deliver any patches, so we stop. The tags stay pullable, but they freeze at their last successful build and receive no further security updates. The full rule is in our ",[247,259,263],{"href":260,"rel":261},"https://github.com/serversideup/docker-php/blob/main/SECURITY.md#component-lifecycle-references",[262],"nofollow","security policy",".",[266,267,268,284],"table",{},[269,270,271],"thead",{},[272,273,274,278,281],"tr",{},[275,276,277],"th",{},"Version",[275,279,280],{},"Last built",[275,282,283],{},"Why",[285,286,287,299],"tbody",{},[272,288,289,293,296],{},[290,291,292],"td",{},"PHP 8.0",[290,294,295],{},"2026-09-03",[290,297,298],{},"Only ever shipped on Debian Bullseye and Alpine 3.16, both now end of life. Upstream stopped building it in November 2023.",[272,300,301,304,306],{},[290,302,303],{},"PHP 7.4",[290,305,295],{},[290,307,308],{},"Same bases as 8.0. Upstream stopped building it in November 2022.",[238,310,311,312,315],{},"Debian Bullseye and Alpine 3.16 were dropped at the same time. Debian 11 LTS ended on 2026-08-31, and the following week Debian removed the Bullseye packages from its mirrors, so ",[242,313,314],{},"apt-get install"," no longer succeeds inside a build. Alpine 3.16 reached end of life on 2024-05-23.",[238,317,318,319,322,323,326,327,330],{},"PHP 8.1 is end of life as well, but we still build it on ",[242,320,321],{},"bookworm",", ",[242,324,325],{},"trixie",", and ",[242,328,329],{},"alpine3.22"," because those bases still receive security updates. It will stop when they do.",[238,332,333,334,337,338,264],{},"If you are on a frozen version, move to a supported PHP version and base from ",[247,335,336],{"href":33},"Choosing an image",". See ",[247,339,342],{"href":340,"rel":341},"https://github.com/serversideup/docker-php/blob/main/SECURITY.md#eol-versions-and-the-legacy-modernization-path",[262],"EOL versions and the legacy-modernization path",[344,345,347],"h3",{"id":346},"installing-packages-on-a-frozen-bullseye-image","Installing packages on a frozen Bullseye image",[349,350,352],"warning",{"title":351},"Unsupported, and it does not make the image safer",[238,353,354,355,358,359,362,363,366,367,369],{},"The ",[242,356,357],{},"7.4"," and ",[242,360,361],{},"8.0"," tags, and any ",[242,364,365],{},"-bullseye"," tag, are frozen. Debian no longer publishes security updates for Bullseye, so nothing below gets you a patched package. It only lets ",[242,368,314],{}," work again so you can add a dependency while you finish migrating.",[238,371,372,373,358,376,379,380,383,384,387,388,394,395,264],{},"Debian moved the Bullseye packages off its regular mirrors after the LTS period ended. The ",[242,374,375],{},"bullseye",[242,377,378],{},"bullseye-updates"," suites now live on ",[242,381,382],{},"archive.debian.org",". The ",[242,385,386],{},"bullseye-security"," suite is not on the archive yet, so the last copy of it is only available from ",[247,389,393],{"href":390,"rel":391,"target":392},"https://snapshot.debian.org/",[262],"_blank","snapshot.debian.org",", Debian's archive of every past mirror state. Its release file has expired, which is why apt must be told to skip the expiry check. This is the same approach ",[247,396,399],{"href":397,"rel":398,"target":392},"https://github.com/mlocati/docker-php-extension-installer/pull/1320",[262],"docker-php-extension-installer uses",[238,401,402,403,406],{},"The frozen images ship an ",[242,404,405],{},"install-php-extensions"," from before that fix, so download the current release before installing extensions.",[408,409,414],"pre",{"className":410,"code":411,"filename":412,"language":413,"meta":10,"style":10},"language-dockerfile shiki shiki-themes github-dark","FROM serversideup/php:7.4-fpm-nginx\n\nUSER root\n\nRUN printf '%s\\n' \\\n      'deb http://archive.debian.org/debian bullseye main' \\\n      'deb http://archive.debian.org/debian bullseye-updates main' \\\n      'deb http://snapshot.debian.org/archive/debian-security/20260903T220410Z bullseye-security main' \\\n      > /etc/apt/sources.list \\\n    && printf 'Acquire::Check-Valid-Until \"false\";\\n' > /etc/apt/apt.conf.d/99no-check-valid-until \\\n    && docker-php-serversideup-install-php-ext-installer 2.11.15\n\nRUN docker-php-serversideup-dep-install-debian \"mariadb-client\" \\\n    && install-php-extensions intl\n\nUSER www-data\n","Dockerfile","dockerfile",[242,415,416,429,436,445,450,473,481,489,497,503,519,525,530,543,549,554],{"__ignoreMap":10},[417,418,421,425],"span",{"class":419,"line":420},"line",1,[417,422,424],{"class":423},"snl16","FROM",[417,426,428],{"class":427},"s95oV"," serversideup/php:7.4-fpm-nginx\n",[417,430,432],{"class":419,"line":431},2,[417,433,435],{"emptyLinePlaceholder":434},true,"\n",[417,437,439,442],{"class":419,"line":438},3,[417,440,441],{"class":423},"USER",[417,443,444],{"class":427}," root\n",[417,446,448],{"class":419,"line":447},4,[417,449,435],{"emptyLinePlaceholder":434},[417,451,453,456,459,463,467,470],{"class":419,"line":452},5,[417,454,455],{"class":423},"RUN",[417,457,458],{"class":427}," printf ",[417,460,462],{"class":461},"sU2Wk","'%s",[417,464,466],{"class":465},"sDLfK","\\n",[417,468,469],{"class":461},"'",[417,471,472],{"class":427}," \\\n",[417,474,476,479],{"class":419,"line":475},6,[417,477,478],{"class":461},"      'deb http://archive.debian.org/debian bullseye main'",[417,480,472],{"class":427},[417,482,484,487],{"class":419,"line":483},7,[417,485,486],{"class":461},"      'deb http://archive.debian.org/debian bullseye-updates main'",[417,488,472],{"class":427},[417,490,492,495],{"class":419,"line":491},8,[417,493,494],{"class":461},"      'deb http://snapshot.debian.org/archive/debian-security/20260903T220410Z bullseye-security main'",[417,496,472],{"class":427},[417,498,500],{"class":419,"line":499},9,[417,501,502],{"class":427},"      > /etc/apt/sources.list \\\n",[417,504,506,509,512,514,516],{"class":419,"line":505},10,[417,507,508],{"class":427},"    && printf ",[417,510,511],{"class":461},"'Acquire::Check-Valid-Until \"false\";",[417,513,466],{"class":465},[417,515,469],{"class":461},[417,517,518],{"class":427}," > /etc/apt/apt.conf.d/99no-check-valid-until \\\n",[417,520,522],{"class":419,"line":521},11,[417,523,524],{"class":427},"    && docker-php-serversideup-install-php-ext-installer 2.11.15\n",[417,526,528],{"class":419,"line":527},12,[417,529,435],{"emptyLinePlaceholder":434},[417,531,533,535,538,541],{"class":419,"line":532},13,[417,534,455],{"class":423},[417,536,537],{"class":427}," docker-php-serversideup-dep-install-debian ",[417,539,540],{"class":461},"\"mariadb-client\"",[417,542,472],{"class":427},[417,544,546],{"class":419,"line":545},14,[417,547,548],{"class":427},"    && install-php-extensions intl\n",[417,550,552],{"class":419,"line":551},15,[417,553,435],{"emptyLinePlaceholder":434},[417,555,557,559],{"class":419,"line":556},16,[417,558,441],{"class":423},[417,560,561],{"class":427}," www-data\n",[238,563,564,565,567,568,570,571,573,574,264],{},"Once Debian publishes ",[242,566,386],{}," on ",[242,569,382],{},", replace the ",[242,572,393],{}," line with ",[242,575,576],{},"deb http://archive.debian.org/debian-security bullseye-security main",[238,578,579,580,583],{},"The Alpine 3.16 tags need no changes. Alpine keeps every past release on its mirrors, so ",[242,581,582],{},"apk add"," still works. The packages are just as unpatched.",[251,585,587],{"id":586},"version-4-version-5-migration","Version 4 → Version 5 Migration",[238,589,590,591,594,595,598,599,264],{},"Version 5 is about production polish. Setting ",[242,592,593],{},"PHP_OPCACHE_ENABLE=1"," now gives you tuned defaults instead of PHP's stock values, Laravel Octane runs with our production Caddyfile, ",[242,596,597],{},"TRUSTED_PROXY"," works the same way on every web server, and FrankenPHP logs follow Caddy's defaults for stream and format. PHP 7.4 and 8.0 are no longer built. See ",[247,600,254],{"href":601},"#dropped-php-versions",[238,603,604,605,608,609,264],{},"The breaking changes below are in the order most people will notice them. The first two are OPcache and FrankenPHP logging. The rest only matter if you call ",[242,606,607],{},"session_start()"," yourself, customize S6 Overlay in your Dockerfile, or run Laravel Octane with a custom ",[242,610,611],{},"FRANKENPHP_CONFIG",[238,613,614,615,264],{},"If you want to stay on Version 4 while you review the changes, pin your image tag to the last v4 release. Version-pinned tags are never rebuilt, so you will not receive security updates until you move to v5. See ",[247,616,618],{"href":617},"/docs/getting-started/upgrade-guide#how-our-releases-work","how our releases work",[408,620,626],{"className":621,"code":622,"filename":623,"highlights":624,"language":625,"meta":10,"style":10},"language-yml shiki shiki-themes github-dark","services:\n  php:\n    image: serversideup/php:8.5-fpm-nginx-v4.5.1\n","compose.yml",[438],"yml",[242,627,628,637,644],{"__ignoreMap":10},[417,629,630,634],{"class":419,"line":420},[417,631,633],{"class":632},"s4JwU","services",[417,635,636],{"class":427},":\n",[417,638,639,642],{"class":419,"line":431},[417,640,641],{"class":632},"  php",[417,643,636],{"class":427},[417,645,648,651,654],{"class":646,"line":438},[419,647],"highlight",[417,649,650],{"class":632},"    image",[417,652,653],{"class":427},": ",[417,655,656],{"class":461},"serversideup/php:8.5-fpm-nginx-v4.5.1\n",[344,658,660],{"id":659},"why-we-changed-opcache","Why we changed OPcache",[238,662,663,664,669,670,675,676],{},"Most people start with these images in development, so OPcache stays off by default to keep your edits showing up instantly. But when you flip it on for production, the settings behind it should be the ones you would have picked yourself after reading the docs. They were not. Version 4 checked every cached file for changes every two seconds, shipped PHP's stock memory sizes, and documented an environment variable that did nothing. Version 5 fixes all of that with the values from ",[247,665,668],{"href":666,"rel":667,"target":392},"https://symfony.com/doc/current/performance.html#configure-opcache-for-maximum-performance",[262],"Symfony's performance guide",", which says \"The default OPcache configuration is not suited for Symfony applications.\" ",[247,671,674],{"href":672,"rel":673,"target":392},"https://frankenphp.dev/docs/performance/",[262],"FrankenPHP's performance guide"," points to the same page \"even if you don't use Symfony.\" ",[247,677,679],{"href":678},"/docs/guide/production-performance-tuning#php-opcache","Read the production performance tuning guide →",[344,681,683],{"id":682},"why-we-changed-frankenphp-logging","Why we changed FrankenPHP logging",[238,685,686,687,690,691,696,697,700,701,704,705,713,714,716,717],{},"Version 4 sent FrankenPHP's logs to ",[242,688,689],{},"stdout"," to mirror the access log convention of our NGINX and Apache variations. Caddy's access log is a named logger that shares the same ",[247,692,695],{"href":693,"rel":694,"target":392},"https://caddyserver.com/docs/logging",[262],"structured format"," and default output as its runtime log, though, with a level on every entry. Caddy, the official FrankenPHP image, and Laravel Octane all expect that output on ",[242,698,699],{},"stderr",". Version 4 also forced the human-readable ",[242,702,703],{},"console"," format, which Caddy ",[247,706,709,710,712],{"href":707,"rel":708,"target":392},"https://caddyserver.com/docs/caddyfile/directives/log#format",[262],"only picks by itself when ",[242,711,699],{}," is a terminal",". A container has no terminal, so Caddy, the official FrankenPHP image, and Octane all write JSON there, and forcing ",[242,715,703],{}," put ANSI color codes into every log pipeline. Version 5 follows both upstream defaults, which means Octane works without any special handling. ",[247,718,720],{"href":719},"/docs/getting-started/default-configurations#logging","Read how we approach logging →",[344,722,724],{"id":723},"breaking-changes-in-version-5","Breaking changes in Version 5",[726,727,728],"caution",{},[238,729,730],{},"Each of these changes behavior for a configuration that works on Version 4. Read them before you update your image tag.",[732,733,735,738,739],"h4",{"id":734},"php_opcache_validate_timestamps-now-defaults-to-0",[242,736,737],{},"PHP_OPCACHE_VALIDATE_TIMESTAMPS"," now defaults to ",[242,740,741],{},"0",[238,743,744],{},"With OPcache enabled, PHP files are now cached until the container restarts. PHP no longer checks the filesystem for changes on every request. This is the correct setting for code that is built into the image, which is how we recommend deploying.",[238,746,747,748,750,751,755],{},"You are affected if you set ",[242,749,593],{}," ",[752,753,754],"strong",{},"and"," any of these apply:",[757,758,759,763,775],"ul",{},[760,761,762],"li",{},"Your code is mounted as a volume and you edit it in place",[760,764,765,766,770,771,774],{},"You follow the ",[247,767,769],{"href":768},"/docs/framework-guides/wordpress/using-wordpress-with-docker#volume-based-approach-traditional","volume-based WordPress approach"," and update with ",[242,772,773],{},"git pull",", WP-CLI, or SFTP",[760,776,777,778,781],{},"You run commands like ",[242,779,780],{},"docker exec php artisan optimize"," against a live container and expect the web workers to pick up the new files",[238,783,784,785,788],{},"The fix is one of two things: restart the container after code changes (recommended), or set ",[242,786,787],{},"PHP_OPCACHE_VALIDATE_TIMESTAMPS=1"," to restore the Version 4 behavior.",[732,790,792,738,795,358,797,800,801],{"id":791},"caddy_log_output-now-defaults-to-stderr-and-caddy_log_format-to-auto",[242,793,794],{},"CADDY_LOG_OUTPUT",[242,796,699],{},[242,798,799],{},"CADDY_LOG_FORMAT"," to ",[242,802,803],{},"auto",[238,805,806,807,809,810,812,813,815,816,819],{},"FrankenPHP logs, including request logs, now go to ",[242,808,699],{}," instead of ",[242,811,689],{},", and inside a container they are JSON instead of ",[242,814,703],{}," lines. ",[242,817,818],{},"docker logs",", Docker Compose, and Kubernetes show both streams, so most setups will only notice the format.",[238,821,822],{},"You are affected if:",[757,824,825,834,840],{},[760,826,827,828,831,832],{},"You separate the streams yourself, for example with ",[242,829,830],{},"2>/dev/null"," or a log driver rule that only captures ",[242,833,689],{},[760,835,836,837,839],{},"You parse the ",[242,838,703],{}," lines, for example with a regular expression in your log pipeline",[760,841,842,843,846,847,850],{},"You read FrankenPHP logs by eye with ",[242,844,845],{},"docker compose logs"," or ",[242,848,849],{},"docker service logs"," and prefer the colored lines",[238,852,853,854,857,858,358,861,864,865,867],{},"The fix is one of two things: keep the defaults and let your log pipeline read the ",[242,855,856],{},"level"," of each JSON entry (recommended), or restore the Version 4 look with ",[242,859,860],{},"CADDY_LOG_FORMAT=console",[242,862,863],{},"CADDY_LOG_OUTPUT=stdout",". Do not use either override with Laravel Octane, which only relays ",[242,866,699],{}," and only parses JSON.",[732,869,871,738,874],{"id":870},"php_session_cookie_httponly-now-defaults-to-on",[242,872,873],{},"PHP_SESSION_COOKIE_HTTPONLY",[242,875,876],{},"On",[238,878,879,880,888],{},"PHP ",[247,881,884,885],{"href":882,"rel":883,"target":392},"https://www.php.net/manual/en/session.security.ini.php",[262],"recommends ",[242,886,887],{},"session.cookie_httponly=On"," for production, so the images now ship it that way. The flag stops browser scripts from reading PHP's native session cookie. Laravel, Symfony, and WordPress manage their own session cookies and are not affected.",[238,890,891,892,894,895,897,898,901],{},"You are affected if your app calls ",[242,893,607],{}," directly ",[752,896,754],{}," reads the session cookie from JavaScript. Set ",[242,899,900],{},"PHP_SESSION_COOKIE_HTTPONLY=Off"," to keep the Version 4 behavior.",[732,903,905,906],{"id":904},"s6-overlay-dependencies-moved-to-dependenciesd","S6 Overlay dependencies moved to ",[242,907,908],{},"dependencies.d/",[238,910,354,911,358,914,917,918,920,921,926,927,930,931,934],{},[242,912,913],{},"fpm-nginx",[242,915,916],{},"fpm-apache"," variations now declare service dependencies with the ",[242,919,908],{}," directory that ",[247,922,925],{"href":923,"rel":924,"target":392},"https://github.com/just-containers/s6-overlay#writing-a-service-script",[262],"S6 Overlay documents",", instead of the deprecated flat ",[242,928,929],{},"dependencies"," file. This is what fixed a race where ",[242,932,933],{},"php-fpm"," could start before its pool user was written when the container runs as root.",[238,936,937,938,941,942,322,944,947,948,951,952,954,955,957],{},"You are affected if your Dockerfile appends lines to ",[242,939,940],{},"/etc/s6-overlay/s6-rc.d/\u003Cservice>/dependencies"," for ",[242,943,933],{},[242,945,946],{},"nginx",", or ",[242,949,950],{},"apache2",". S6 ignores that file once ",[242,953,908],{}," exists. Create an empty file in ",[242,956,908],{}," instead:",[408,959,961],{"className":410,"code":960,"filename":412,"language":413,"meta":10,"style":10},"RUN touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/my-service\n",[242,962,963],{"__ignoreMap":10},[417,964,965,967],{"class":419,"line":420},[417,966,455],{"class":423},[417,968,969],{"class":427}," touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/my-service\n",[238,971,972,973],{},"Stock images and rootless containers are not affected. ",[247,974,975],{"href":201},"Learn more about start-up script dependencies →",[732,977,979,980,983,984],{"id":978},"s6-overlay-user-bundle-moved-to-user-bundlesd","S6 Overlay ",[242,981,982],{},"user"," bundle moved to ",[242,985,986],{},"user-bundles.d/",[238,988,989,990,995,996,358,998,1001,1002,809,1005,1008,1009,1012,1013,1016],{},"Version 5 ships ",[247,991,994],{"href":992,"rel":993,"target":392},"https://github.com/just-containers/s6-overlay/blob/v3.2.3.2/CHANGELOG.md",[262],"S6 Overlay v3.2.3.2",", which defines the ",[242,997,982],{},[242,999,1000],{},"user2"," bundles in ",[242,1003,1004],{},"/etc/s6-overlay/user-bundles.d/",[242,1006,1007],{},"/etc/s6-overlay/s6-rc.d/",". The images follow that layout, so ",[242,1010,1011],{},"/etc/s6-overlay/s6-rc.d/user"," no longer exists. ",[242,1014,1015],{},"docker-php-serversideup-s6-init"," already writes to the new location.",[238,1018,1019,1020,1023],{},"You are affected if your Dockerfile creates ",[242,1021,1022],{},"/etc/s6-overlay/s6-rc.d/user/contents.d/\u003Cservice>"," to start your own service. Create the empty file in the new directory instead:",[408,1025,1027],{"className":410,"code":1026,"filename":412,"language":413,"meta":10,"style":10},"RUN touch /etc/s6-overlay/user-bundles.d/user/contents.d/my-service\n",[242,1028,1029],{"__ignoreMap":10},[417,1030,1031,1033],{"class":419,"line":420},[417,1032,455],{"class":423},[417,1034,1035],{"class":427}," touch /etc/s6-overlay/user-bundles.d/user/contents.d/my-service\n",[349,1037,1038],{},[238,1039,1040,1041,1043,1044,1046,1047,1049,1050,1053,1054,1057],{},"Do not recreate the old ",[242,1042,1011],{}," directory. When it exists, S6 Overlay ignores ",[242,1045,986],{}," entirely, so ",[242,1048,933],{}," and the web server never start. As the default ",[242,1051,1052],{},"www-data"," user, the container does not boot at all because S6 Overlay tries to write to ",[242,1055,1056],{},"/etc/s6-overlay"," during start up.",[732,1059,1061,1062,1064,1065,1068],{"id":1060},"laravel-octane-the-frankenphp_config-worker-block-and-caddy_global_options-no-longer-apply","Laravel Octane: the ",[242,1063,611],{}," worker block and ",[242,1066,1067],{},"CADDY_GLOBAL_OPTIONS"," no longer apply",[238,1070,1071,1072,1075,1076,1078,1079,1082],{},"Before Version 5, running Octane with our Caddyfile meant adding a ",[242,1073,1074],{},"worker { }"," block to ",[242,1077,611],{}," and routing directives to ",[242,1080,1081],{},"CADDY_PHP_SERVER_OPTIONS",". The Caddyfile now does both when Octane starts FrankenPHP, so keeping the block fails with \"global workers must not have duplicate filenames\". Remove it.",[238,1084,1085,1086,1088,1089,358,1092,1094,1095,1098,1099],{},"Octane also needs the Caddy admin API and sets ",[242,1087,1067],{}," for itself, so ",[242,1090,1091],{},"CADDY_ADMIN",[242,1093,1067],{}," are ignored while Octane runs. Put global options in ",[242,1096,1097],{},"/etc/frankenphp/caddyfile-global.d/"," instead. Nothing changes in classic mode. ",[247,1100,1102],{"href":1101},"/docs/framework-guides/laravel/octane#octane-options","Read the Octane guide →",[732,1104,1106,1107,1110],{"id":1105},"frankenphp-remote_addr-is-now-the-client-ip-resolved-from-trusted-proxies","FrankenPHP: ",[242,1108,1109],{},"REMOTE_ADDR"," is now the client IP resolved from trusted proxies",[238,1112,1113,1114,1117,1118,1120,1121],{},"Version 4 set ",[242,1115,1116],{},"$_SERVER['REMOTE_ADDR']"," to the TCP peer on FrankenPHP, even when Caddy had already worked out the real client IP for the access log. It now matches what Caddy resolved, the same as NGINX and Apache, and Caddy runs in strict mode so a client behind a trusted proxy cannot forge it. You are affected if anything in your app compares ",[242,1119,1109],{}," to a proxy's address. ",[247,1122,1123],{"href":176},"Read the trusted proxies guide →",[732,1125,1127,1128,358,1131],{"id":1126},"frankenphp-mercure-10-rejects-publisher_jwt-and-subscriber_jwt","FrankenPHP: Mercure 1.0 rejects ",[242,1129,1130],{},"publisher_jwt",[242,1132,1133],{},"subscriber_jwt",[238,1135,1136,1137,846,1139,1141,1142,1145,1146,1149,1150,1153,1154,800,1157,1160,1161,1165,1166,1171],{},"Version 5 ships FrankenPHP 1.13, which includes Mercure 1.0. If you enable the Mercure hub with ",[242,1138,1130],{},[242,1140,1133],{},", including through the ",[242,1143,1144],{},"mercure"," array in ",[242,1147,1148],{},"config/octane.php",", FrankenPHP now fails to start. Turn the hub on with the new ",[242,1151,1152],{},"MERCURE_*"," environment variables instead, which work in classic mode and with Octane. If your app still signs 0.x tokens, add ",[242,1155,1156],{},"protocol_version_compatibility 8",[242,1158,1159],{},"MERCURE_EXTRA_DIRECTIVES"," while you migrate. ",[247,1162,1164],{"href":1163},"/docs/image-variations/frankenphp#mercure","Read how to set up Mercure →"," The ",[247,1167,1170],{"href":1168,"rel":1169,"target":392},"https://github.com/dunglas/mercure/blob/v1.0.3/docs/UPGRADE.md",[262],"Mercure 1.0 upgrade guide"," covers the client changes.",[732,1173,1175],{"id":1174},"frankenphp-caddy-limits-request-headers","FrankenPHP: Caddy limits request headers",[238,1177,1178,1179,1184,1185,1188,1189,1191,1192,1195,1196,1199,1200,264],{},"FrankenPHP 1.13 includes ",[247,1180,1183],{"href":1181,"rel":1182,"target":392},"https://github.com/caddyserver/caddy/releases/tag/v2.11.7",[262],"Caddy 2.11.7",". Requests with more than 16 KiB of headers now get a ",[242,1186,1187],{},"431 Request Header Fields Too Large"," response, where Version 4 allowed 1 MB. Large cookies are the usual cause. Headers with a ",[242,1190,264],{}," in their name are now dropped, like headers with a ",[242,1193,1194],{},"_",", because PHP reads both as ",[242,1197,1198],{},"-"," and a client could use them to spoof headers like ",[242,1201,1202],{},"X-Forwarded-For",[732,1204,1106,1206,1209],{"id":1205},"frankenphp-num_threads-no-longer-includes-worker-threads",[242,1207,1208],{},"num_threads"," no longer includes worker threads",[238,1211,1212,1213,1215,1216,1218,1219,1222,1223,1225],{},"If you set ",[242,1214,1208],{}," in ",[242,1217,611],{}," while running workers, including Laravel Octane, FrankenPHP now starts that many threads on top of the worker threads, and fails to start if ",[242,1220,1221],{},"max_threads"," is lower than the total. Lower ",[242,1224,1208],{}," to the number of threads you want for regular requests.",[344,1227,1229],{"id":1228},"fixes","Fixes",[757,1231,1232],{},[760,1233,1234,1237,1238,1241,1242,1245],{},[242,1235,1236],{},"PHP_OPCACHE_FORCE_RESTART_TIMEOUT"," existed in Version 4 but never reached ",[242,1239,1240],{},"php.ini",". It now works. The default of ",[242,1243,1244],{},"180"," matches PHP's own default, so nothing changes unless you had set it to something else.",[344,1247,1249],{"id":1248},"changed-defaults","Changed defaults",[238,1251,1252,1253,1255,1256,1259,1260,264],{},"The OPcache values apply only when ",[242,1254,593],{},", and the memory is only used as files are cached. ",[242,1257,1258],{},"PHP_REALPATH_CACHE_TTL"," is not an OPcache setting and applies whether OPcache is on or off. It comes from the same ",[247,1261,1264],{"href":1262,"rel":1263,"target":392},"https://symfony.com/doc/current/performance.html#configure-the-php-realpath-cache",[262],"Symfony recommendation",[266,1266,1267,1280],{},[269,1268,1269],{},[272,1270,1271,1274,1277],{},[275,1272,1273],{},"Variable",[275,1275,1276],{},"Version 4",[275,1278,1279],{},"Version 5",[285,1281,1282,1297,1314,1331,1348],{},[272,1283,1284,1288,1293],{},[290,1285,1286],{},[242,1287,737],{},[290,1289,1290],{},[242,1291,1292],{},"1",[290,1294,1295],{},[242,1296,741],{},[272,1298,1299,1304,1309],{},[290,1300,1301],{},[242,1302,1303],{},"PHP_OPCACHE_MEMORY_CONSUMPTION",[290,1305,1306],{},[242,1307,1308],{},"128",[290,1310,1311],{},[242,1312,1313],{},"256",[272,1315,1316,1321,1326],{},[290,1317,1318],{},[242,1319,1320],{},"PHP_OPCACHE_INTERNED_STRINGS_BUFFER",[290,1322,1323],{},[242,1324,1325],{},"8",[290,1327,1328],{},[242,1329,1330],{},"32",[272,1332,1333,1338,1343],{},[290,1334,1335],{},[242,1336,1337],{},"PHP_OPCACHE_MAX_ACCELERATED_FILES",[290,1339,1340],{},[242,1341,1342],{},"10000",[290,1344,1345],{},[242,1346,1347],{},"32531",[272,1349,1350,1354,1359],{},[290,1351,1352],{},[242,1353,1258],{},[290,1355,1356],{},[242,1357,1358],{},"120",[290,1360,1361],{},[242,1362,1363],{},"600",[238,1365,1366,358,1368,1370,1371,264],{},[242,1367,794],{},[242,1369,799],{}," apply to the FrankenPHP variation only. See ",[247,1372,683],{"href":1373},"#why-we-changed-frankenphp-logging",[266,1375,1376,1386],{},[269,1377,1378],{},[272,1379,1380,1382,1384],{},[275,1381,1273],{},[275,1383,1276],{},[275,1385,1279],{},[285,1387,1388,1402],{},[272,1389,1390,1394,1398],{},[290,1391,1392],{},[242,1393,794],{},[290,1395,1396],{},[242,1397,689],{},[290,1399,1400],{},[242,1401,699],{},[272,1403,1404,1408,1412],{},[290,1405,1406],{},[242,1407,799],{},[290,1409,1410],{},[242,1411,703],{},[290,1413,1414],{},[242,1415,803],{},[344,1417,1419],{"id":1418},"new-variables","New variables",[757,1421,1422,1437,1448,1454,1463,1477,1489,1498,1506,1532,1551,1557,1574],{},[760,1423,1424,1427,1428,1430,1431,1433,1434,1436],{},[242,1425,1426],{},"PHP_OPCACHE_ENABLE_CLI"," - Whether CLI commands use OPcache when ",[242,1429,593],{},". Defaults to ",[242,1432,1292],{},", which is what Version 4 did. Set it to ",[242,1435,741],{}," to keep OPcache on for the web server only.",[760,1438,1439,1442,1443,264],{},[242,1440,1441],{},"PHP_OPCACHE_PRELOAD"," - Path to a preload script. Symfony generates one for you and ",[247,1444,1447],{"href":1445,"rel":1446,"target":392},"https://symfony.com/doc/current/performance.html#use-the-opcache-class-preloading",[262],"recommends it",[760,1449,1450,1453],{},[242,1451,1452],{},"PHP_OPCACHE_PRELOAD_USER"," - The user to preload as when the container runs as root.",[760,1455,1456,1459,1460,264],{},[242,1457,1458],{},"PHP_DISABLE_FUNCTIONS"," - Comma-separated list of PHP functions to disable. Empty by default because Laravel, Composer, and Symfony Process rely on ",[242,1461,1462],{},"proc_open",[760,1464,1465,358,1468,1471,1472,358,1474,264],{},[242,1466,1467],{},"PHP_FILE_UPLOADS",[242,1469,1470],{},"PHP_MAX_FILE_UPLOADS"," - Turn HTTP file uploads off, or cap how many files one request can carry. Default to ",[242,1473,876],{},[242,1475,1476],{},"20",[760,1478,1479,1482,1483,1486,1487,264],{},[242,1480,1481],{},"PHP_HTML_ERRORS"," - Format on-screen errors as HTML when ",[242,1484,1485],{},"PHP_DISPLAY_ERRORS"," is on. Defaults to ",[242,1488,876],{},[760,1490,1491,1494,1495,264],{},[242,1492,1493],{},"PHP_REALPATH_CACHE_SIZE"," - Size of PHP's realpath cache. Defaults to ",[242,1496,1497],{},"4096K",[760,1499,1500,1502,1503,1505],{},[242,1501,873],{}," - Defaults to ",[242,1504,876],{},". See the breaking change above.",[760,1507,1508,1510,1511,1514,1515,322,1518,947,1521,1524,1525,322,1527,326,1529,264],{},[242,1509,597],{}," - Which proxy IPs to trust for the real client IP: ",[242,1512,1513],{},"cloudflare"," (default), ",[242,1516,1517],{},"sucuri",[242,1519,1520],{},"local",[242,1522,1523],{},"off",". Works on ",[242,1526,913],{},[242,1528,916],{},[242,1530,1531],{},"frankenphp",[760,1533,1534,1537,1538,1541,1542,322,1545,947,1548,1550],{},[242,1535,1536],{},"CADDY_ACME_PROFILE"," - Select a Let's Encrypt certificate profile on FrankenPHP: ",[242,1539,1540],{},"shortlived"," (required for IP-address certificates), ",[242,1543,1544],{},"tlsserver",[242,1546,1547],{},"classic",[242,1549,1523],{}," (default).",[760,1552,1553,1556],{},[242,1554,1555],{},"LARAVEL_OCTANE"," - Set by Octane, not by you. The FrankenPHP Caddyfile uses it to switch into worker mode.",[760,1558,1559,322,1562,322,1565,322,1568,326,1571,1573],{},[242,1560,1561],{},"MERCURE_ENABLED",[242,1563,1564],{},"MERCURE_TRUSTED_ISSUERS",[242,1566,1567],{},"MERCURE_PUBLISHER_JWT_KEY",[242,1569,1570],{},"MERCURE_SUBSCRIBER_JWT_KEY",[242,1572,1159],{}," - Run FrankenPHP's Mercure hub. See the breaking change above.",[760,1575,1576,1579,1580,1583,1584,1430,1587,264],{},[242,1577,1578],{},"AUTORUN_LARAVEL_SKIP_IF_NOT_FOUND"," - Lets the container start when Laravel is not in ",[242,1581,1582],{},"APP_BASE_DIR"," yet, for example before the first ",[242,1585,1586],{},"composer install",[242,1588,1589],{},"false",[238,1591,1592],{},[247,1593,1594],{"href":223},"See the full list of environment variables →",[344,1596,1598],{"id":1597},"new-features-in-version-5","New features in Version 5",[757,1600,1601,1616,1634,1647,1663,1675,1693,1707],{},[760,1602,1603,1606,1607,800,1610,1613,1614],{},[752,1604,1605],{},"Laravel Octane uses our Caddyfile"," - Pass ",[242,1608,1609],{},"--caddyfile=/etc/frankenphp/Caddyfile",[242,1611,1612],{},"octane:start"," and the image switches into worker mode with the same trusted proxy support, security headers, asset caching, SSL modes, and health check as classic mode. ",[247,1615,1102],{"href":121},[760,1617,1618,1621,1622,1624,1625,322,1627,326,1629,1631,1632],{},[752,1619,1620],{},"Trusted proxies on every web server"," - ",[242,1623,597],{}," gives ",[242,1626,913],{},[242,1628,916],{},[242,1630,1531],{}," the same Cloudflare, Sucuri, local, or off behavior, and all three resolve the client IP through more than one Docker hop. ",[247,1633,1123],{"href":176},[760,1635,1636,1639,1640,1642,1643],{},[752,1637,1638],{},"Short-lived and IP-address certificates"," - FrankenPHP can request Let's Encrypt's short-lived profile with ",[242,1641,1536],{},". ",[247,1644,1646],{"href":1645},"/docs/deployment-and-production/configuring-ssl#short-lived--ip-address-certificates","Read about short-lived certificates →",[760,1648,1649,1621,1652,1655,1656,1659,1660],{},[752,1650,1651],{},"Laravel Nightwatch health check",[242,1653,1654],{},"healthcheck-nightwatch"," runs ",[242,1657,1658],{},"php artisan nightwatch:status"," so Docker can watch the agent. ",[247,1661,1662],{"href":117},"Read the Nightwatch guide →",[760,1664,1665,1668,1669,1672,1673],{},[752,1666,1667],{},"Mercure from environment variables"," - Set ",[242,1670,1671],{},"MERCURE_ENABLED=true"," and your JWT keys to run FrankenPHP's Mercure hub, in classic mode or with Octane. ",[247,1674,1164],{"href":1163},[760,1676,1677,1680,1681,1684,1685,1688,1689],{},[752,1678,1679],{},"Caddyfile rules from a folder"," - Drop a ",[242,1682,1683],{},".caddyfile"," into ",[242,1686,1687],{},"/etc/frankenphp/caddyfile-server.d/"," to add headers, redirects, or matchers to your app's site, in classic mode or with Octane. ",[247,1690,1692],{"href":1691},"/docs/image-variations/frankenphp#adding-your-own-caddyfile-rules","Read how to add your own Caddyfile rules →",[760,1694,1695,1702,1703],{},[752,1696,1697,1698,1701],{},"FrankenPHP redacts the ",[242,1699,1700],{},"authorization"," query parameter"," - Request logs never contain the JWT that Mercure 0.x subscribers pass in the URL, in every log format. ",[247,1704,1706],{"href":1705},"/docs/image-variations/frankenphp#logging","Read about FrankenPHP logging →",[760,1708,1709,1712,1713,358,1716,1719,1720],{},[752,1710,1711],{},"Every image is tested before it is published"," - Each image is started on ",[242,1714,1715],{},"amd64",[242,1717,1718],{},"arm64"," and checked before it reaches Docker Hub. If one image fails, nothing from that build is published. ",[247,1721,1723],{"href":1722},"/docs/getting-started/contributing#what-happens-when-you-open-a-pull-request","Read what happens when you open a pull request →",[344,1725,1727],{"id":1726},"v5-migration-checklist","V5 Migration Checklist",[732,1729,1731],{"id":1730},"docker-compose","Docker Compose",[757,1733,1734,1737,1745,1751,1758,1766,1781,1796],{},[760,1735,1736],{},"Update the image tag",[760,1738,1739,1740,1742,1743],{},"If your code is mounted as a volume with ",[242,1741,593],{},", either turn OPcache off for that environment or add ",[242,1744,787],{},[760,1746,1747,1748,1750],{},"If you deploy WordPress on a volume, add ",[242,1749,787],{}," or restart the container after updates made outside the WordPress admin",[760,1752,1753,1754,1757],{},"Replace any ",[242,1755,1756],{},"docker exec ... artisan optimize"," style deployment steps with a container restart",[760,1759,1760,1761,1763,1764],{},"If your app calls ",[242,1762,607],{}," itself and reads the session cookie from JavaScript, add ",[242,1765,900],{},[760,1767,1768,1769,1771,1772,1774,1775,1777,1778,1780],{},"If you run FrankenPHP and something reads only ",[242,1770,689],{},", add ",[242,1773,863],{},". If something parses the ",[242,1776,703],{}," lines, or you prefer them when reading logs by eye, add ",[242,1779,860],{},". Skip both if you run Laravel Octane",[760,1782,1783,1784,1786,1787,1789,1790,1792,1793,1795],{},"If you run Laravel Octane, add ",[242,1785,1609],{}," to your ",[242,1788,1612],{}," command and remove any ",[242,1791,611],{}," worker block or ",[242,1794,1081],{}," you added to make Octane work",[760,1797,1798,1799,1801,1802,1804,1805,1807,1808],{},"If you run the Mercure hub on FrankenPHP, set ",[242,1800,1671],{}," and your keys with the ",[242,1803,1152],{}," variables, and remove the ",[242,1806,1144],{}," array from ",[242,1809,1148],{},[732,1811,412],{"id":413},[757,1813,1814,1829,1839],{},[760,1815,1816,1817,941,1819,322,1821,947,1823,1825,1826,1828],{},"If you append to ",[242,1818,940],{},[242,1820,933],{},[242,1822,946],{},[242,1824,950],{},", move each line to an empty file in that service's ",[242,1827,908],{}," directory",[760,1830,1831,1832,1834,1835,1838],{},"If you create ",[242,1833,1022],{}," to start your own S6 service, create it in ",[242,1836,1837],{},"/etc/s6-overlay/user-bundles.d/user/contents.d/"," instead",[760,1840,1841,1842,1844,1845,1848,1849,1852],{},"If you add ",[242,1843,1441],{},", prefer setting it on the running service rather than as an ",[242,1846,1847],{},"ENV"," in the Dockerfile, so build steps like ",[242,1850,1851],{},"RUN composer install"," do not depend on the preload script",[251,1854,1856],{"id":1855},"version-3-version-4-migration","Version 3 → Version 4 Migration",[238,1858,1859,1860,1863],{},"Version 3 to Version 4 is a much easier migration compared to previous versions. There are ",[752,1861,1862],{},"no breaking changes",", so you can simply update your image tag to the latest version and take advantage of the new features.",[344,1865,1867],{"id":1866},"new-features-in-version-4","New Features in Version 4",[238,1869,1870],{},"This release focused on expanding image variations, improving Laravel automations, and enhancing the developer experience. Here are the key features:",[757,1872,1873,1879,1885,1905,1913,1926,1946,1959],{},[760,1874,1875,1878],{},[752,1876,1877],{},"FrankenPHP variation"," - A new production-ready FrankenPHP variation with intelligent defaults, flexible environment configuration, native health checks, and support for Debian and Alpine operating systems.",[760,1880,1881,1884],{},[752,1882,1883],{},"Revamped documentation site"," - Completely rewritten documentation with improved navigation, better examples, and a modern user experience.",[760,1886,1887,1890,1891,1894,1895,322,1898,1901,1902,264],{},[752,1888,1889],{},"Enhanced Laravel automations"," - Refactored to use ",[242,1892,1893],{},"php artisan optimize"," by default (following Laravel best practices), with support for migration modes (",[242,1896,1897],{},"fresh",[242,1899,1900],{},"refresh","), database connection selection, seeding options, and easier debugging with ",[242,1903,1904],{},"AUTORUN_DEBUG",[760,1906,1907,1910,1911],{},[752,1908,1909],{},"Expanded environment variables"," - 25+ new environment variables for fine-tuning PHP, NGINX, Apache, and FrankenPHP configurations. ",[247,1912,1594],{"href":223},[760,1914,1915,1918,1919,358,1922,1925],{},[752,1916,1917],{},"Improved health checks"," - Better container startup detection using ",[242,1920,1921],{},"start-period",[242,1923,1924],{},"start-interval"," for more accurate health readings.",[760,1927,1928,1931,1932,1935,1936,322,1939,947,1942,1945],{},[752,1929,1930],{},"IPv6 support for NGINX"," - Control IP listening protocols with ",[242,1933,1934],{},"NGINX_LISTEN_IP_PROTOCOL"," (supports ",[242,1937,1938],{},"ipv4",[242,1940,1941],{},"ipv6",[242,1943,1944],{},"all",").",[760,1947,1948,1621,1951,1954,1955,1958],{},[752,1949,1950],{},"Enhanced file permissions script",[242,1952,1953],{},"docker-php-serversideup-set-file-permissions"," now includes automated service detection and support for multiple directories with the ",[242,1956,1957],{},"--dir"," flag.",[760,1960,1961,1964,1965,358,1967,1969],{},[752,1962,1963],{},"Quieter logs"," - Health check requests no longer appear in access logs for ",[242,1966,913],{},[242,1968,916],{}," variations.",[344,1971,1973],{"id":1972},"quality-of-life-improvements","Quality of Life Improvements",[757,1975,1976,1986,2000,2006],{},[760,1977,1978,1981,1982,1985],{},[752,1979,1980],{},"Startup scripts"," - Improved handling of ",[242,1983,1984],{},"entrypoint.d"," scripts with better error handling and a redesigned container startup info display.",[760,1987,1988,1991,1992,1995,1996,358,1998,1969],{},[752,1989,1990],{},"FPM process control"," - Default changed to ",[242,1993,1994],{},"ondemand"," for even lower resource usage in ",[242,1997,913],{},[242,1999,916],{},[760,2001,2002,2005],{},[752,2003,2004],{},"Better Apache logs"," - Access logs now include \"Referer\" and \"User Agent\" for better debugging.",[760,2007,2008,2011,2012,2015,2016,2019,2020,2023],{},[752,2009,2010],{},"NGINX improvements"," - Added ",[242,2013,2014],{},"absolute_redirect off;"," for better proxy compatibility, fixed ",[242,2017,2018],{},"svgz"," handling with Symfony's asset mapper, and allowed ",[242,2021,2022],{},"robots.txt"," to be dynamically generated by PHP.",[344,2025,2027],{"id":2026},"v4-migration-checklist","V4 Migration Checklist",[238,2029,2030],{},"Since there are no breaking changes, the migration is straightforward:",[732,2032,2034],{"id":2033},"update-your-images","Update Your Images",[238,2036,2037],{},"Simply update your image tags to the latest version. For example:",[408,2039,2041],{"className":621,"code":2040,"filename":623,"language":625,"meta":10,"style":10},"services:\n  php:\n    image: serversideup/php:8.5-fpm-nginx\n",[242,2042,2043,2049,2055],{"__ignoreMap":10},[417,2044,2045,2047],{"class":419,"line":420},[417,2046,633],{"class":632},[417,2048,636],{"class":427},[417,2050,2051,2053],{"class":419,"line":431},[417,2052,641],{"class":632},[417,2054,636],{"class":427},[417,2056,2057,2059,2061],{"class":419,"line":438},[417,2058,650],{"class":632},[417,2060,653],{"class":427},[417,2062,2063],{"class":461},"serversideup/php:8.5-fpm-nginx\n",[238,2065,2066],{},"No other changes are required unless you want to take advantage of new features.",[732,2068,2070],{"id":2069},"optional-leverage-new-features","Optional: Leverage New Features",[238,2072,2073],{},[752,2074,2075],{},"Consider enabling Laravel optimizations (if using Laravel):",[408,2077,2079],{"className":621,"code":2078,"filename":623,"language":625,"meta":10,"style":10},"services:\n  php:\n    image: serversideup/php:8.5-fpm-nginx\n    environment:\n      AUTORUN_ENABLED: \"true\"\n      AUTORUN_LARAVEL_OPTIMIZE: \"true\"\n",[242,2080,2081,2087,2093,2101,2108,2118],{"__ignoreMap":10},[417,2082,2083,2085],{"class":419,"line":420},[417,2084,633],{"class":632},[417,2086,636],{"class":427},[417,2088,2089,2091],{"class":419,"line":431},[417,2090,641],{"class":632},[417,2092,636],{"class":427},[417,2094,2095,2097,2099],{"class":419,"line":438},[417,2096,650],{"class":632},[417,2098,653],{"class":427},[417,2100,2063],{"class":461},[417,2102,2103,2106],{"class":419,"line":447},[417,2104,2105],{"class":632},"    environment",[417,2107,636],{"class":427},[417,2109,2110,2113,2115],{"class":419,"line":452},[417,2111,2112],{"class":632},"      AUTORUN_ENABLED",[417,2114,653],{"class":427},[417,2116,2117],{"class":461},"\"true\"\n",[417,2119,2120,2123,2125],{"class":419,"line":475},[417,2121,2122],{"class":632},"      AUTORUN_LARAVEL_OPTIMIZE",[417,2124,653],{"class":427},[417,2126,2117],{"class":461},[238,2128,2129],{},[752,2130,2131],{},"Try the new FrankenPHP variation:",[408,2133,2135],{"className":621,"code":2134,"filename":623,"language":625,"meta":10,"style":10},"services:\n  php:\n    image: serversideup/php:8.5-frankenphp\n    ports:\n      - 80:8080\n      - 443:8443\n",[242,2136,2137,2143,2149,2158,2165,2173],{"__ignoreMap":10},[417,2138,2139,2141],{"class":419,"line":420},[417,2140,633],{"class":632},[417,2142,636],{"class":427},[417,2144,2145,2147],{"class":419,"line":431},[417,2146,641],{"class":632},[417,2148,636],{"class":427},[417,2150,2151,2153,2155],{"class":419,"line":438},[417,2152,650],{"class":632},[417,2154,653],{"class":427},[417,2156,2157],{"class":461},"serversideup/php:8.5-frankenphp\n",[417,2159,2160,2163],{"class":419,"line":447},[417,2161,2162],{"class":632},"    ports",[417,2164,636],{"class":427},[417,2166,2167,2170],{"class":419,"line":452},[417,2168,2169],{"class":427},"      - ",[417,2171,2172],{"class":461},"80:8080\n",[417,2174,2175,2177],{"class":419,"line":475},[417,2176,2169],{"class":427},[417,2178,2179],{"class":461},"443:8443\n",[238,2181,2182],{},"That's it! Version 4 is designed to be a smooth, non-breaking upgrade that gives you more flexibility and features when you need them.",[251,2184,2186],{"id":2185},"version-2-version-3-migration","Version 2 → Version 3 Migration",[238,2188,2189,2190,2193],{},"If you're an existing user of our v2 images, be sure that your current configurations are NOT set to use the latest images. To do this, you can lock your images into the ",[242,2191,2192],{},"v2.2.1"," tag. This will ensure that you're not automatically upgraded to the v3 images.",[238,2195,2196,2197,2200,2201,2203,2204,2210],{},"For example, if you are using ",[242,2198,2199],{},"8.2-fpm-nginx",", you would change your ",[242,2202,623],{}," file to use the ",[247,2205,2208],{"href":2206,"rel":2207,"target":392},"https://hub.docker.com/r/serversideup/php/tags?page=1&name=2.2.1",[262],[242,2209,2192],{}," tag:",[408,2212,2215],{"className":621,"code":2213,"filename":623,"highlights":2214,"language":625,"meta":10,"style":10},"services:\n  php:\n    image: serversideup/php:8.2-fpm-nginx\n    ports:\n      - 80:80\n    volumes:\n      - .:/var/www/html\n",[438],[242,2216,2217,2223,2229,2239,2245,2252,2259],{"__ignoreMap":10},[417,2218,2219,2221],{"class":419,"line":420},[417,2220,633],{"class":632},[417,2222,636],{"class":427},[417,2224,2225,2227],{"class":419,"line":431},[417,2226,641],{"class":632},[417,2228,636],{"class":427},[417,2230,2232,2234,2236],{"class":2231,"line":438},[419,647],[417,2233,650],{"class":632},[417,2235,653],{"class":427},[417,2237,2238],{"class":461},"serversideup/php:8.2-fpm-nginx\n",[417,2240,2241,2243],{"class":419,"line":447},[417,2242,2162],{"class":632},[417,2244,636],{"class":427},[417,2246,2247,2249],{"class":419,"line":452},[417,2248,2169],{"class":427},[417,2250,2251],{"class":461},"80:80\n",[417,2253,2254,2257],{"class":419,"line":475},[417,2255,2256],{"class":632},"    volumes",[417,2258,636],{"class":427},[417,2260,2261,2263],{"class":419,"line":483},[417,2262,2169],{"class":427},[417,2264,2265],{"class":461},".:/var/www/html\n",[408,2267,2270],{"className":621,"code":2268,"filename":623,"highlights":2269,"language":625,"meta":10,"style":10},"services:\n  php:\n    image: serversideup/php:8.2-fpm-nginx-v2.2.1\n    ports:\n      - 80:80\n    volumes:\n      - .:/var/www/html\n",[438],[242,2271,2272,2278,2284,2294,2300,2306,2312],{"__ignoreMap":10},[417,2273,2274,2276],{"class":419,"line":420},[417,2275,633],{"class":632},[417,2277,636],{"class":427},[417,2279,2280,2282],{"class":419,"line":431},[417,2281,641],{"class":632},[417,2283,636],{"class":427},[417,2285,2287,2289,2291],{"class":2286,"line":438},[419,647],[417,2288,650],{"class":632},[417,2290,653],{"class":427},[417,2292,2293],{"class":461},"serversideup/php:8.2-fpm-nginx-v2.2.1\n",[417,2295,2296,2298],{"class":419,"line":447},[417,2297,2162],{"class":632},[417,2299,636],{"class":427},[417,2301,2302,2304],{"class":419,"line":452},[417,2303,2169],{"class":427},[417,2305,2251],{"class":461},[417,2307,2308,2310],{"class":419,"line":475},[417,2309,2256],{"class":632},[417,2311,636],{"class":427},[417,2313,2314,2316],{"class":419,"line":483},[417,2315,2169],{"class":427},[417,2317,2265],{"class":461},[238,2319,2320,2321,2324],{},"All you need to do is add ",[242,2322,2323],{},"-v2.2.1"," to the end of the image tag. This will ensure that you're not automatically upgraded to the v3 images.",[344,2326,2328],{"id":2327},"new-features-in-version-3","New Features in Version 3",[238,2330,2331],{},"We've been busy overhauling our PHP Docker Images to make them more production-ready and easier to use. Here are some of the new features we've added:",[757,2333,2334,2340,2346,2352,2364,2374,2398,2404],{},[760,2335,2336,2339],{},[752,2337,2338],{},"Based on official PHP Images"," - We're now building an improved developer experience on top of the official PHP Docker images.",[760,2341,2342,2345],{},[752,2343,2344],{},"Unprivileged by default"," - We're now running our images as an unprivileged user by default. This is a huge step forward in security and compatibility.",[760,2347,2348,2351],{},[752,2349,2350],{},"PHP 8.4 support"," - We're now shipping the latest and greatest.",[760,2353,2354,2357,2358,809,2361,264],{},[752,2355,2356],{},"Pin to the exact minor version"," - Pin your app to the exact minor version of PHP that you want to use. This means you can pin to ",[242,2359,2360],{},"8.2.12",[242,2362,2363],{},"8.2",[760,2365,2366,2369,2370,2373],{},[752,2367,2368],{},"Easier start up script customization"," - We now have a folder called ",[242,2371,2372],{},"/etc/entrypoint.d"," that allows you to easily customize your container with scripts. Just put them in numerical order and we'll execute any shell script you want. No S6 Overlay knowledge required.",[760,2375,2376,2379,2380,322,2383,322,2386,322,2389,322,2392,326,2395],{},[752,2377,2378],{},"Expanded Laravel Automations"," - We added automations to run ",[242,2381,2382],{},"config:cache",[242,2384,2385],{},"route:cache",[242,2387,2388],{},"view:cache",[242,2390,2391],{},"event:cache",[242,2393,2394],{},"migrate --force --isolated",[242,2396,2397],{},"storage:link",[760,2399,2400,2403],{},[752,2401,2402],{},"NGINX Unit Support"," - We're offering NGINX Unit as a variation as an alternative to PHP-FPM. This allows you to run PHP applications without the need for a webserver like NGINX or Apache to run with PHP-FPM.",[760,2405,2406,2409],{},[752,2407,2408],{},"Available on GitHub Packages"," - We're now publishing our images to GitHub Packages. This means you can use our images without needing to authenticate with Docker Hub.",[344,2411,2413],{"id":2412},"breaking-changes-in-version-3","Breaking changes in Version 3",[726,2415,2416],{},[238,2417,2418],{},"The following changes are considered to be \"breaking changes\" and will require you to make changes to your application.",[732,2420,2422],{"id":2421},"ubuntu-is-no-longer-used-as-a-base-image","Ubuntu is no longer used as a base image",[238,2424,2425],{},"We now use Debian or Alpine as our base OS (because we're using the official PHP images as a base). This is a huge change, but we're confident this will be the best direction moving forward.",[732,2427,2429,2432],{"id":2428},"ppaondrejphp-is-no-longer-used",[242,2430,2431],{},"ppa:ondrej/php"," is no longer used",[238,2434,2435,2436,2438,2439,2442],{},"Since we're using PHP.net as the \"official source of truth\" for getting our PHP versions, this means we're also dropping support for the ",[242,2437,2431],{}," repository. If you're using things like ",[242,2440,2441],{},"apt-get install php-redis"," you will need to change your method of installing PHP extensions.",[238,2444,2445],{},[247,2446,2447],{"href":197},"Learn how to install your own PHP extension →",[732,2449,2451,2454],{"id":2450},"webuser-is-no-longer-being-used",[242,2452,2453],{},"webuser"," is no longer being used",[238,2456,2457,2458,2460,2461,2464,2465,2467,2468,2471,2472,2474,2475,2478,2479,264],{},"We used to add a user called ",[242,2459,2453],{}," with the UID of ",[242,2462,2463],{},"9999"," with shell permissions. To increase security, we're now using the ",[242,2466,1052],{}," user and group that is built into the official PHP images. If you have mounted volumes, you will need to ",[242,2469,2470],{},"chown"," the files to match the ID of the ",[242,2473,1052],{}," user and groups. For Debian, this is ",[242,2476,2477],{},"33:33"," and for Alpine, this is ",[242,2480,2481],{},"82:82",[732,2483,2485],{"id":2484},"nginx-and-apache-listen-on-8080-http-and-8443-https-by-default","NGINX and Apache listen on 8080 (HTTP) and 8443 (HTTPS) by default",[238,2487,2488,2489,358,2492,809,2495,358,2498,264],{},"Our images are now unprivileged by default. This is a major step forward in security and compatibility. Since we are unprivileged by default, we lose the ability to mount on ports less than 1024. If you're using NGINX or Apache, you will need to update your port mappings to use ",[242,2490,2491],{},"8080",[242,2493,2494],{},"8443",[242,2496,2497],{},"80",[242,2499,2500],{},"443",[238,2502,2503],{},[247,2504,2506],{"href":2505},"/docs/getting-started/default-configurations#unprivileged-by-default","Learn more about this change →",[732,2508,2510,2511,358,2514,2517],{"id":2509},"s6-overlay-is-only-used-in-fpm-apache-and-fpm-nginx-images","S6 Overlay is only used in ",[242,2512,2513],{},"*-fpm-apache",[242,2515,2516],{},"*-fpm-nginx"," images",[238,2519,2520,2521,358,2523,2525,2526,2528],{},"Due to compatibility issues, we only use S6 Overlay in our ",[242,2522,2513],{},[242,2524,2516],{}," images. If you were using S6 Overlay for our other variations (cli, fpm, etc), you will need to migrate your scripts to use the new ",[242,2527,2372],{}," folder.",[732,2530,2532,2535,2536,2538],{"id":2531},"ssl_mode-is-now-set-to-off-by-default-http-only",[242,2533,2534],{},"SSL_MODE"," is now set to ",[242,2537,1523],{}," by default (HTTP only)",[238,2540,2541],{},"Running end-to-end SSL by default created more problems than good. By default, we're now shipping HTTP-only by default with the option for people to turn this on.",[732,2543,2545,2535,2548,2550],{"id":2544},"autorun_enabled-is-now-set-to-false-by-default",[242,2546,2547],{},"AUTORUN_ENABLED",[242,2549,1589],{}," by default.",[238,2552,2553,2554,264],{},"Having this set to \"true\" by default also created more problems than good. If you want to use any of the Laravel Automation Scripts, be sure to set this to ",[242,2555,2556],{},"true",[732,2558,2560],{"id":2559},"msmtp-is-no-longer-included-in-the-images","MSMTP is no longer included in the images",[238,2562,2563],{},"For security and image size reasons, we removed MSMTP from the images. If you need to send emails, use an external SMTP service like Postmark/Sendgrid/Mailgun. You can also extend the image yourself to include MSMTP specifically for your use case.",[732,2565,2567],{"id":2566},"variable-deprecations","Variable deprecations",[757,2569,2570,2578,2587,2600,2609],{},[760,2571,2572,2575,2576],{},[242,2573,2574],{},"WEB_APP_DIRECTORY"," has now been renamed to ",[242,2577,1582],{},[760,2579,2580,2583,2584],{},[242,2581,2582],{},"DEBUG_OUTPUT"," has been removed for in favor of ",[242,2585,2586],{},"LOG_OUTPUT_LEVEL=debug",[760,2588,2589,2592,2593,2596,2597],{},[242,2590,2591],{},"PUID"," & ",[242,2594,2595],{},"PGID"," are no longer used because it requires root privileges. See the ",[247,2598,2599],{"href":172},"new way to set the UID and GID →",[760,2601,2602,2592,2605,2608],{},[242,2603,2604],{},"MSMTP_RELAY_SERVER_HOSTNAME",[242,2606,2607],{},"MSMTP_RELAY_SERVER_PORT"," are no longer used because MSMTP is no longer included in the images.",[760,2610,2611,2614,2615],{},[242,2612,2613],{},"PHP_POOL_NAME"," has been renamed to ",[242,2616,2617],{},"PHP_FPM_POOL_NAME",[344,2619,2621],{"id":2620},"v3-migration-checklist","V3 Migration Checklist",[238,2623,2624],{},"Here is a good list to perform the V3 migration.",[732,2626,2628],{"id":2627},"repository","Repository",[757,2630,2631],{},[760,2632,2633],{},"Ensure you're committing to a test environment",[732,2635,1731],{"id":2636},"docker-compose-1",[757,2638,2639,2642,2647,2655],{},[760,2640,2641],{},"Update the image name (if applicable)",[760,2643,2644,2645],{},"Check each environment variable exists and is set to a proper value ",[247,2646,1594],{"href":223},[760,2648,2649,2650,358,2652,2654],{},"Ensure you updated the ports to ",[242,2651,2491],{},[242,2653,2494],{}," for NGINX, Apache, and Unit",[760,2656,2657,2658,2660],{},"Consider adding ",[242,2659,593],{}," to your production environment for increased performance",[732,2662,412],{"id":2663},"dockerfile-1",[757,2665,2666,2669,2675,2678,2686],{},[760,2667,2668],{},"Update the base image name (if applicable)",[760,2670,2671,2672,2674],{},"Remove any ",[242,2673,2431],{}," references",[760,2676,2677],{},"Remove any Ubuntu specific commands",[760,2679,2680,2681,2683,2684],{},"Ensure all extensions are installed with the ",[242,2682,405],{}," command ",[247,2685,2447],{"href":197},[760,2687,2688,2689,2692,2693,2696],{},"Ensure your ",[242,2690,2691],{},"COPY"," commands are copying with the correct permissions (i.e. ",[242,2694,2695],{},"--chown=www-data:www-data",")",[732,2698,2700],{"id":2699},"cicd","CI/CD",[238,2702,2703,2704,2706,2707,2710,2711,358,2714,1786,2717,2720],{},"If you're running ",[242,2705,913],{}," (or similar) on a runner that's running as your builds as ",[242,2708,2709],{},"root",", you may need to add ",[242,2712,2713],{},"user = www-data",[242,2715,2716],{},"group = www-data",[242,2718,2719],{},"php-fpm.conf"," file so you can bring FPM up correctly.",[238,2722,2723],{},"If you have to run things as root in CI, you can do this with a multi stage build and set the targets:",[408,2725,2727],{"className":410,"code":2726,"filename":412,"language":413,"meta":10,"style":10},"############################################\n# Base Image\n############################################\n\n# Learn more about the Server Side Up PHP Docker Images at:\n# https://serversideup.net/open-source/docker-php/\nFROM serversideup/php:8.4-fpm-nginx AS base\n\n## Uncomment if you need to install additional PHP extensions\n# USER root\n# RUN install-php-extensions bcmath gd\n\n############################################\n# Development Image\n############################################\nFROM base AS development\n\n# We can pass USER_ID and GROUP_ID as build arguments\n# to ensure the www-data user has the same UID and GID\n# as the user running Docker.\nARG USER_ID\nARG GROUP_ID\n\n# Switch to root so we can set the user ID and group ID\nUSER root\nRUN docker-php-serversideup-set-id www-data $USER_ID:$GROUP_ID  && \\\n    docker-php-serversideup-set-file-permissions --owner $USER_ID:$GROUP_ID\nUSER www-data\n\n############################################\n# CI image\n############################################\nFROM base AS ci\n\n# Sometimes CI images need to run as root\nUSER root\n\n############################################\n# Production Image\n############################################\nFROM base AS deploy\nCOPY --chown=www-data:www-data . /var/www/html\nUSER www-data\n",[242,2728,2729,2735,2740,2744,2748,2753,2758,2771,2775,2780,2785,2790,2794,2798,2803,2807,2819,2824,2830,2836,2842,2851,2859,2864,2870,2877,2885,2891,2898,2903,2908,2914,2919,2931,2936,2942,2949,2954,2959,2965,2970,2982,2990],{"__ignoreMap":10},[417,2730,2731],{"class":419,"line":420},[417,2732,2734],{"class":2733},"sAwPA","############################################\n",[417,2736,2737],{"class":419,"line":431},[417,2738,2739],{"class":2733},"# Base Image\n",[417,2741,2742],{"class":419,"line":438},[417,2743,2734],{"class":2733},[417,2745,2746],{"class":419,"line":447},[417,2747,435],{"emptyLinePlaceholder":434},[417,2749,2750],{"class":419,"line":452},[417,2751,2752],{"class":2733},"# Learn more about the Server Side Up PHP Docker Images at:\n",[417,2754,2755],{"class":419,"line":475},[417,2756,2757],{"class":2733},"# https://serversideup.net/open-source/docker-php/\n",[417,2759,2760,2762,2765,2768],{"class":419,"line":483},[417,2761,424],{"class":423},[417,2763,2764],{"class":427}," serversideup/php:8.4-fpm-nginx ",[417,2766,2767],{"class":423},"AS",[417,2769,2770],{"class":427}," base\n",[417,2772,2773],{"class":419,"line":491},[417,2774,435],{"emptyLinePlaceholder":434},[417,2776,2777],{"class":419,"line":499},[417,2778,2779],{"class":2733},"## Uncomment if you need to install additional PHP extensions\n",[417,2781,2782],{"class":419,"line":505},[417,2783,2784],{"class":2733},"# USER root\n",[417,2786,2787],{"class":419,"line":521},[417,2788,2789],{"class":2733},"# RUN install-php-extensions bcmath gd\n",[417,2791,2792],{"class":419,"line":527},[417,2793,435],{"emptyLinePlaceholder":434},[417,2795,2796],{"class":419,"line":532},[417,2797,2734],{"class":2733},[417,2799,2800],{"class":419,"line":545},[417,2801,2802],{"class":2733},"# Development Image\n",[417,2804,2805],{"class":419,"line":551},[417,2806,2734],{"class":2733},[417,2808,2809,2811,2814,2816],{"class":419,"line":556},[417,2810,424],{"class":423},[417,2812,2813],{"class":427}," base ",[417,2815,2767],{"class":423},[417,2817,2818],{"class":427}," development\n",[417,2820,2822],{"class":419,"line":2821},17,[417,2823,435],{"emptyLinePlaceholder":434},[417,2825,2827],{"class":419,"line":2826},18,[417,2828,2829],{"class":2733},"# We can pass USER_ID and GROUP_ID as build arguments\n",[417,2831,2833],{"class":419,"line":2832},19,[417,2834,2835],{"class":2733},"# to ensure the www-data user has the same UID and GID\n",[417,2837,2839],{"class":419,"line":2838},20,[417,2840,2841],{"class":2733},"# as the user running Docker.\n",[417,2843,2845,2848],{"class":419,"line":2844},21,[417,2846,2847],{"class":423},"ARG",[417,2849,2850],{"class":427}," USER_ID\n",[417,2852,2854,2856],{"class":419,"line":2853},22,[417,2855,2847],{"class":423},[417,2857,2858],{"class":427}," GROUP_ID\n",[417,2860,2862],{"class":419,"line":2861},23,[417,2863,435],{"emptyLinePlaceholder":434},[417,2865,2867],{"class":419,"line":2866},24,[417,2868,2869],{"class":2733},"# Switch to root so we can set the user ID and group ID\n",[417,2871,2873,2875],{"class":419,"line":2872},25,[417,2874,441],{"class":423},[417,2876,444],{"class":427},[417,2878,2880,2882],{"class":419,"line":2879},26,[417,2881,455],{"class":423},[417,2883,2884],{"class":427}," docker-php-serversideup-set-id www-data $USER_ID:$GROUP_ID  && \\\n",[417,2886,2888],{"class":419,"line":2887},27,[417,2889,2890],{"class":427},"    docker-php-serversideup-set-file-permissions --owner $USER_ID:$GROUP_ID\n",[417,2892,2894,2896],{"class":419,"line":2893},28,[417,2895,441],{"class":423},[417,2897,561],{"class":427},[417,2899,2901],{"class":419,"line":2900},29,[417,2902,435],{"emptyLinePlaceholder":434},[417,2904,2906],{"class":419,"line":2905},30,[417,2907,2734],{"class":2733},[417,2909,2911],{"class":419,"line":2910},31,[417,2912,2913],{"class":2733},"# CI image\n",[417,2915,2917],{"class":419,"line":2916},32,[417,2918,2734],{"class":2733},[417,2920,2922,2924,2926,2928],{"class":419,"line":2921},33,[417,2923,424],{"class":423},[417,2925,2813],{"class":427},[417,2927,2767],{"class":423},[417,2929,2930],{"class":427}," ci\n",[417,2932,2934],{"class":419,"line":2933},34,[417,2935,435],{"emptyLinePlaceholder":434},[417,2937,2939],{"class":419,"line":2938},35,[417,2940,2941],{"class":2733},"# Sometimes CI images need to run as root\n",[417,2943,2945,2947],{"class":419,"line":2944},36,[417,2946,441],{"class":423},[417,2948,444],{"class":427},[417,2950,2952],{"class":419,"line":2951},37,[417,2953,435],{"emptyLinePlaceholder":434},[417,2955,2957],{"class":419,"line":2956},38,[417,2958,2734],{"class":2733},[417,2960,2962],{"class":419,"line":2961},39,[417,2963,2964],{"class":2733},"# Production Image\n",[417,2966,2968],{"class":419,"line":2967},40,[417,2969,2734],{"class":2733},[417,2971,2973,2975,2977,2979],{"class":419,"line":2972},41,[417,2974,424],{"class":423},[417,2976,2813],{"class":427},[417,2978,2767],{"class":423},[417,2980,2981],{"class":427}," deploy\n",[417,2983,2985,2987],{"class":419,"line":2984},42,[417,2986,2691],{"class":423},[417,2988,2989],{"class":427}," --chown=www-data:www-data . /var/www/html\n",[417,2991,2993,2995],{"class":419,"line":2992},43,[417,2994,441],{"class":423},[417,2996,561],{"class":427},[732,2998,3000],{"id":2999},"productionstaging-servers","Production/Staging Servers",[757,3002,3003,3018],{},[760,3004,3005,3006,3008,3009,3011,3012,3014,3015],{},"Update all host volume file permissions to match the ",[242,3007,1052],{}," UID/GID (",[242,3010,2477],{}," for Debian, ",[242,3013,2481],{}," for Alpine) ",[247,3016,3017],{"href":172},"Learn how to manage file permissions",[760,3019,3020,3021,2696],{},"If you're running Docker Swarm with host volume mounts, we created a script that could potentially help (",[247,3022,3025],{"href":3023,"rel":3024},"https://github.com/serversideup/docker-volume-change-permission-script",[262],"change-volume-permissions.sh",[732,3027,3029],{"id":3028},"deployment","Deployment",[757,3031,3032,3035],{},[760,3033,3034],{},"CI/CD with valid tests is always encouraged",[760,3036,3037],{},"After completing all steps above, you're now ready to deploy the new images",[3039,3040,3041],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}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 pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":10,"searchDepth":431,"depth":431,"links":3043},[3044,3047,3057,3062],{"id":253,"depth":431,"text":254,"children":3045},[3046],{"id":346,"depth":438,"text":347},{"id":586,"depth":431,"text":587,"children":3048},[3049,3050,3051,3052,3053,3054,3055,3056],{"id":659,"depth":438,"text":660},{"id":682,"depth":438,"text":683},{"id":723,"depth":438,"text":724},{"id":1228,"depth":438,"text":1229},{"id":1248,"depth":438,"text":1249},{"id":1418,"depth":438,"text":1419},{"id":1597,"depth":438,"text":1598},{"id":1726,"depth":438,"text":1727},{"id":1855,"depth":431,"text":1856,"children":3058},[3059,3060,3061],{"id":1866,"depth":438,"text":1867},{"id":1972,"depth":438,"text":1973},{"id":2026,"depth":438,"text":2027},{"id":2185,"depth":431,"text":2186,"children":3063},[3064,3065,3066],{"id":2327,"depth":438,"text":2328},{"id":2412,"depth":438,"text":2413},{"id":2620,"depth":438,"text":2621},"Step-by-step guides for migrating between major versions of the serversideup/php Docker images.","md",null,{"head":3071,"layout":7},{"title":3072},"Major version migrations - Docker PHP - Server Side Up",{"title":179,"description":3067},"8GZQmVLORkoK-xxu4pqygP7Q7vU8z9UqmqWw2hRbUzI",[3076,3078],{"title":175,"path":176,"stem":177,"description":3077,"children":-1},"Learn how to configure trusted proxies to get accurate client IP addresses when running behind load balancers, CDNs, or reverse proxies.",{"title":183,"path":184,"stem":185,"description":3079,"children":-1},"The settings that matter for PHP performance in production, where our defaults come from, and how to measure and tune them for your application.",1793279097843]