Laravel PHP version

Laravel PHP version requirements, and switching PHP safely

Laravel 13 runs on PHP 8.3 to 8.5 and Laravel 12 on 8.2 to 8.5, so both can move to a PHP release fixed for PHP2Shell. Laravel 10 and 11 are past their security end dates. After the switch, restart queue workers and other long-running processes, which keep running the PHP build they started with.

Last reviewed against Laravel's release notes and support policy, the Composer documentation and php.net.

Requirements table

PHP versions supported by each Laravel release

Laravel ships one major release a year and supports each with 18 months of bug fixes and two years of security fixes. The PHP range belongs to the major version your application is built on.

Release PHP Released Bug fixes until Security fixes until Status on 27 October 2026 PHP2Shell-fixed releases it can run
Laravel 13 8.3 to 8.5 17 March 2026 Q3 2027 17 March 2028 Bug and security fixes (13.38.0, 20 October 2026) 8.3.36, 8.4.27 or 8.5.12
Laravel 12 8.2 to 8.5 24 February 2025 13 August 2026 24 February 2027 Security fixes only (12.70.1, 21 October 2026) 8.2.35, 8.3.36, 8.4.27 or 8.5.12
Laravel 11 8.2 to 8.4 12 March 2024 3 September 2025 12 March 2026 End of life 8.2.35, 8.3.36 or 8.4.27
Laravel 10 8.1 to 8.3 14 February 2023 6 August 2024 4 February 2025 End of life 8.2.35 or 8.3.36

An application still on PHP 8.1 is on Laravel 10 or older, the last release that accepted 8.1, and 8.1 has no PHP2Shell release from the PHP project. The PHP branch dates are on PHP end of life dates, and other frameworks and applications are on the PHP version requirements hub.

Sources: Laravel release notes and support policy; Laravel framework releases

composer.json

Where a Laravel app declares its PHP version

Two entries in composer.json decide which PHP your application will accept and which PHP Composer plans for. They do different jobs, and mixing them up is how a build that passed on a laptop fails on a server.

{
    "require": {
        "php": "^8.3",
        "laravel/framework": "^13.0"
    },
    "config": {
        "platform": {
            "php": "8.3.36"
        }
    }
}

require.php is the range the application supports. A fresh Laravel 13 application ships with ^8.3, which Composer reads as 8.3 or later but below 9.0. By default, Composer also writes a PHP version check into the autoloader it generates, based on the requirements of the packages it installed.

config.platform.php makes Composer resolve packages as if that exact PHP were installed. Set it to the lowest PHP your servers run and move it in the same commit as the server change.

Two commands either side of the change

# Before: which locked packages would refuse PHP 8.5?
composer prohibits php 8.5

# After, on each server: real PHP and extensions against what is installed
composer check-platform-reqs --no-dev

prohibits (also called why-not) names every locked package whose constraints rule out the PHP version you give it. check-platform-reqs ignores the platform pin and compares the server's real PHP and extensions with the packages installed there. Composer itself recommends running it as part of deployment whenever a platform pin is set.

Commit composer.lock so every server installs the set of packages you tested.

Sources: Composer basic usage; Composer config; Composer commands; Composer version constraints

Every PHP process

Four places PHP runs in a Laravel deployment

Many Laravel applications run more than one kind of PHP process. Each of these has to end up on the fixed build, and each gets there differently.

3D network of laptops and servers, for web requests through PHP-FPM

Web requests through PHP-FPM

Laravel's sample nginx configuration hands requests to a socket named after the PHP branch, such as php8.3-fpm.sock. A patch within the branch keeps the socket and needs only an FPM restart. A branch change means pointing nginx at the new branch's socket.

3D loop of cloud, gears and padlock, for long-lived queue workers

Queue workers

Laravel describes queue:work processes as long-lived: they hold the booted application in memory and do not see changes until restarted. A worker started last week is still last week's PHP process, whatever has been installed since.

3D magnifying glass with a 24/7 badge, for processes that keep running

Horizon, Octane and Reverb

Horizon runs its own pool of queue workers, and Octane and Reverb are long-running servers in their own right. Laravel's deployment guide lists Octane and Reverb among the services to reload after a deployment, and Horizon's guide asks for horizon:terminate.

3D desk of monitors, for Artisan and Composer on the command line

Artisan, Composer and deploy scripts

These use the command-line PHP, which can be a different build from the one PHP-FPM runs. A bare php in a script or Supervisor entry means whichever binary is first on the path, so a server with two branches installed can run the old one without anyone noticing.

Sources: Laravel deployment; Laravel queues; Laravel Horizon

Long-running processes

Getting workers onto the new PHP

Laravel's restart commands ask each worker to finish its current job and exit. Your process monitor then starts a replacement, and the replacement is what loads the new PHP. Without a monitor, the workers simply stop.

Restart the workers

# Laravel 12 and 13: restart queue workers, Reverb and Octane in one go
php artisan reload

# The individual commands
php artisan queue:restart
php artisan horizon:terminate

queue:restart signals through your cache store, and workers check for it between jobs, so a worker busy with a long job finishes it first. That is the intended behaviour; it is also why the restart is not instant.

When the branch changes, edit the monitor too

Laravel's Supervisor example starts workers with a bare php. If you are moving branch, name the new binary explicitly. On Debian, each branch installs its own, such as /usr/bin/php8.4.

[program:laravel-worker]
command=/usr/bin/php8.4 /var/www/app/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true

Supervisor's restart does not re-read configuration files, so after editing the entry apply it with reread and update:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl status

Sources: Laravel: reloading services; Laravel: queue workers and deployment; Supervisor: running supervisorctl

Upgrade steps

Moving a Laravel application to a fixed PHP release

Installing PHP itself is covered in how to update PHP and, for Ubuntu, Debian and the RHEL family, the Linux guide. The steps below are the Laravel side of the change.

  1. Confirm the framework version. php artisan about lists the Laravel version and the command-line PHP version together. Read the PHP range for that release from the table.
  2. Choose the target. Within the same branch, take its fixed release. If you are changing branch, pick one inside your Laravel release's range, and prefer 8.3 or later so PHP 8.2's end of life on 31 December 2026 does not force a second move.
  3. Test before you touch production. Raise config.platform.php, run composer prohibits php with the new version, update what it names, and run your test suite on the target PHP.
  4. Install the fixed PHP on every server. Web servers, queue and Horizon hosts, and any machine that runs scheduled Artisan commands all need it, including ones behind a load balancer that are easy to forget.
  5. Repoint anything that names a branch. For a branch change, update the nginx socket path and the binary in Supervisor entries, then run supervisorctl reread and update.
  6. Restart, in order. Restart PHP-FPM, then run php artisan reload (or queue:restart and horizon:terminate) so every long-running process is replaced.
  7. Verify. Work through the checks below on each server.

Confirm it worked

Proving every Laravel process moved

  • The command line reports the fixed release.
    php artisan about --only=environment
    The Environment section's PHP Version line should read 8.3.36, 8.4.27, 8.5.12 or later on its branch, or a distribution package your vendor lists as fixed.
  • Composer agrees. composer check-platform-reqs --no-dev passes on each server.
  • The web tier is on the same build. The FPM service you restarted is the one nginx points at. On RHEL-family servers, dnf needs-restarting lists processes that still need a restart after the update.
  • Workers came back. sudo supervisorctl status shows each worker program running again after the restart, and jobs are being processed.
  • The exposure is reviewed. If the application was reachable on a vulnerable build, work through the compromise checks; if a server cannot be patched soon, read whether to take it offline.

Hosting a PHP application you did not build? The Nextcloud and Moodle pages cover their own PHP rules, including their command-line tools.

Laravel questions

Laravel PHP version FAQs

What PHP version does Laravel 12 need?

PHP 8.2 to 8.5. Laravel 12's bug fixes ended on 13 August 2026 and its security fixes run until 24 February 2027. Any of 8.2.35, 8.3.36, 8.4.27 or 8.5.12 closes PHP2Shell; 8.3 or later avoids PHP 8.2's end of life on 31 December 2026 and is also what Laravel 13 needs.

Can Laravel 10 run on PHP 8.4?

Not as a supported pairing. Laravel lists PHP 8.1 to 8.3 for Laravel 10, so 8.3.36 is the newest PHP2Shell release it supports. Laravel 10's security fixes ended on 4 February 2025, so the framework upgrade, to 12 or 13, should follow the PHP patch quickly.

What does config.platform.php do in composer.json?

It tells Composer to resolve dependencies as if the given PHP version were installed, whatever PHP the machine running Composer has. That keeps a developer laptop on PHP 8.5 from locking packages a PHP 8.3 server cannot run. Composer warns that it also stops the real PHP being checked, so run composer check-platform-reqs on the server as part of each deployment.

Do I need to restart queue workers after updating PHP?

Yes. A queue worker is one long-running PHP process, and Laravel's documentation says workers do not pick up changes until they restart. Run php artisan reload, or php artisan queue:restart and php artisan horizon:terminate, and let your process monitor start fresh workers on the new build.

Will updating Laravel fix PHP2Shell?

No. Laravel is a Composer package that runs on PHP; updating it cannot replace the PHP runtime. Laravel 13.38.0 and 12.70.1 are current framework releases, but the PHP2Shell fix only arrives with a patched PHP build on the server.

PHP2Shell explained

3D rocket illustration above the call to find every exposed PHP server

Know your exposure

Find every PHP server you run

Patching the server you know about is the easy part. A vulnerability scan finds the forgotten staging site, the old microsite and the appliance with PHP inside, then tells you which ones are still exposed.