PHP 8.1 went end of life on 31 December 2025 and received no PHP2Shell release. A Laravel application still running on it has to move to 8.3 or 8.4. The PHP change itself takes minutes. The time goes into three other places: the framework version, the dependencies, and the server configuration that still points at 8.1 after the switch. This checklist covers all three.
1. Check the framework version first
Each Laravel release supports a range of PHP versions, and the ranges are narrower than people expect:
- Laravel 9 supports PHP 8.0 to 8.2 and left security support in February 2024.
- Laravel 10 supports PHP 8.1 to 8.3 and left security support in February 2025.
- Laravel 11 requires PHP 8.2 or newer and left security support in March 2026.
- Laravel 12 and 13 are the current targets; 12 requires PHP 8.2 or newer, 13 requires PHP 8.3 or newer.
Check the exact dates on the Laravel release notes before planning, because they move. The practical consequence: an application on Laravel 9 cannot simply be switched to PHP 8.3; it needs a framework upgrade first, which is the larger project. Laravel 10 can run on 8.3, which makes it a workable stepping stone, but it is itself out of support, so plan the next step too.
Find your version with php artisan --version or in composer.lock.
2. Let Composer tell you what blocks the move
Before touching the server:
composer why-not php 8.3
composer why-not php 8.4
Every package listed is one that declares it does not support that PHP version. Some need an update, some have been abandoned and need a replacement. Also look at composer.json for a config.platform.php entry; if it pins an old version, Composer resolves dependencies as if the server still ran 8.1, and you will install packages that cannot run on 8.3.
Then update on a branch, with the target PHP installed locally:
composer update --with-all-dependencies
Review the diff of composer.lock. Major version jumps in packages deserve a read of their upgrade notes.
3. Run the test suite under the new version
If the application has tests, run them on the target PHP before anything else. If it does not, this is the moment to write a handful of smoke tests for the key journeys: login, the main forms, any payment or export, scheduled commands. They will pay for themselves during this upgrade and the next one.
Static analysis helps too. PHPStan or Larastan at a modest level will flag most type-related breakages that only appear at runtime otherwise, and Rector has rule sets for the 8.2 to 8.4 changes.
4. The deprecations that matter
From 8.1 to 8.4 the changes most Laravel codebases trip over:
- Dynamic properties (deprecated in 8.2). Eloquent models are unaffected because they implement
__getand__set, but plain classes that set undeclared properties, often in older service classes or value objects, now emit deprecation notices and will break in a future release. Declare the properties or add the#[\AllowDynamicProperties]attribute as a stopgap. ${var}string interpolation (deprecated in 8.2). Use{$var}.utf8_encode()andutf8_decode()(deprecated in 8.2). Usemb_convert_encoding().- Passing
nullto non-nullable internal function parameters (deprecated since 8.1, still a frequent source of notices). Typical:trim($request->input('name'))when the input is absent. - Implicitly nullable parameters (deprecated in 8.4):
function f(string $x = null)should becomefunction f(?string $x = null).
Set error_reporting(E_ALL) and log deprecations on staging so they surface before production.
5. The server pieces that still say 8.1
This is where upgrades "succeed" and the site still runs on the old version. Search the server for 8.1:
- Web server: Nginx
fastcgi_pass unix:/run/php/php8.1-fpm.sockor an Apache FPM proxy pointing at the old socket. - The
phpbinary on the path:update-alternatives --config phpon Debian and Ubuntu, otherwise cron jobs andartisancommands keep running 8.1. - Cron: scheduler entries that call
/usr/bin/php8.1 artisan schedule:run. - Queue workers: Supervisor or systemd units that run
php8.1 artisan queue:work, Horizon included. After the change,php artisan queue:restartso running workers pick up the new binary. - Octane, FrankenPHP or RoadRunner: the server process has to be restarted with the new runtime.
- OPcache: restart PHP-FPM after deploying so the opcode cache is cleared.
- Docker: the base image tag (
php:8.1-fpmtophp:8.3-fpmor8.4), plus any extension install lines that reference the version.
Once the old packages are no longer referenced anywhere, remove them, so a future mistake cannot fall back to an unpatched runtime.
6. Switch, verify, watch
Use a maintenance window, take a backup, switch the FPM pool and the web server configuration, restart, and verify with php -v, a phpinfo page and the application's own health endpoint. Then watch the logs for a few days: deprecation notices from rarely used paths tend to appear a week later.
If you would like a second pair of eyes on the inventory or the upgrade itself, send us the Laravel version, the current PHP version and where the application is hosted.
