Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Laravel 12 Real World Migration Guide from Laravel 10

By Kokil Thapa | Last reviewed: August 2026

If your application still runs Laravel 10, you are carrying framework debt that compounds every quarter. Laravel 10 reached end-of-life for feature updates, security patches are winding down, and third-party packages increasingly assume Laravel 11 or 12 with PHP 8.2 or higher. This Laravel 12 Real World Migration Guide from Laravel 10 walks through the incremental path I use on production apps — not a theoretical changelog rewrite, but the audit, upgrade, test, and deploy sequence that keeps client sites online. Before you touch composer.json, read what changed in Laravel 12 so you know which breaking changes actually affect your codebase.

Why should you upgrade from Laravel 10 to Laravel 12 in 2026?

Staying on Laravel 10 in 2026 means you inherit risk without gaining speed. Laravel 12 (current major, minimum PHP 8.2) is primarily a maintenance-focused release built on the Laravel 11 application skeleton — cleaner bootstrap, streamlined config publishing, and continued first-class support for Vite 6.x front-end builds. Laravel 11 already removed deprecated APIs that Laravel 10 still tolerated; Laravel 12 finishes that cleanup.

On legal-tech portals and eCommerce systems I maintain, the practical reasons to migrate are operational, not cosmetic:

  • Security coverage. Framework and dependency advisories target supported majors. Composer audit warnings on Laravel 10 packages become harder to resolve each month.
  • Package compatibility. Spatie, Livewire, Filament, Sanctum, and Horizon releases now assume Laravel 11+ and PHP 8.2.
  • Developer velocity. Laravel 11+ skeleton reduces boilerplate; Laravel Pint, PHPStan, and CI pipelines integrate cleanly with the modern structure.
  • Hosting alignment. Ubuntu 24.04 servers ship PHP 8.3/8.4; running PHP 8.1 for Laravel 10 forces you to maintain an EOL runtime.

A common mistake is treating this as a single composer update weekend. In practice, plan two upgrade windows — 10→11 and 11→12 — with a full regression pass after each hop. That matches how Laravel 11 restructured the default application and keeps rollback boundaries clear.

Laravel 10 → 12 Migration RoadmapLaravel 10PHP 8.1+PHP 8.2+Server upgradeLaravel 11Structure changesLaravel 12Package refreshProduction deployTests + opcache reloadParallel work during migration• Composer audit + package bumps• PHPUnit / Pest test expansion• Vite 6 asset rebuild• Staging mirror validation
Laravel 12 Real World Migration Guide from Laravel 10 — recommended two-hop upgrade path with PHP prep first
StageLaravel 10 (current)Laravel 11 (first hop)Laravel 12 (target)
PHP minimum8.18.28.2 (8.3/8.4 recommended)
Support status (2026)EOL / security onlySupportedCurrent major
App skeletonFull config files, Kernel.phpSlim bootstrap, middleware in bootstrap/app.phpSame as 11, minor defaults
Front-end defaultVite (Mix legacy)ViteVite 6.x
Migration effortHigh (structure + packages)Low (mostly composer bump)
Rollback complexityModerateLow if 11 is stable first

What PHP version do you need before migrating to Laravel 12?

Laravel 12 requires PHP 8.2 or higher. PHP 8.4 is the latest stable in 2026; PHP 8.3 is widely deployed on production VPS hosts. Do not jump straight to 8.4 on day one if your host or CI image lacks it — 8.2 satisfies the framework requirement and keeps risk bounded.

Verify local and server PHP before Composer

php -v
composer check-platform-reqs
php -m | grep -E 'mbstring|openssl|pdo|tokenizer|xml|ctype|json|bcmath'

On Ubuntu 22.04/24.04 servers I administer, I install PHP via the ondrej/php PPA and run multiple versions side by side during transition:

sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php8.3-fpm php8.3-cli php8.3-mysql php8.3-xml php8.3-mbstring php8.3-curl php8.3-zip php8.3-redis
sudo update-alternatives --set php /usr/bin/php8.3

Fix application code for PHP 8.2+ deprecations

Before changing Laravel, scan your app with static analysis. I've caught dozens of latent issues this way on client upgrades:

composer require --dev phpstan/phpstan larastan/larastan --with-all-dependencies
vendor/bin/phpstan analyse app --level=5

Watch for: dynamic properties on plain classes (PHP 8.2 deprecates them — use typed properties or #[\AllowDynamicProperties] sparingly), nullable implicit parameters, and ${var} string interpolation removed in PHP 8.2+. Run your existing PHPUnit or Pest suite on the new PHP version while still on Laravel 10 — if tests fail here, they will fail louder after the framework bump.

Pre-Migration Audit ChecklistClone production snapshotcomposer auditPackage Laravel 11/12 supportCustom service providersDocument queues, cron, webhooks, paymentsGreen light to upgrade branch
Audit composer packages, integrations, and infrastructure before starting the Laravel 12 Real World Migration Guide from Laravel 10

How do you upgrade Laravel 10 to Laravel 11 step by step?

The 10→11 hop carries the real weight. Laravel 11 reshaped the default application structure — fewer default config files, middleware registered in bootstrap/app.php, and removal of app/Http/Kernel.php in fresh installs. Existing Laravel 10 apps keep their Kernel until you migrate manually. Follow the official upgrade guide at laravel.com/docs/12/upgrade alongside these production steps.

Step 1: Create an upgrade branch and pin dependencies

git checkout -b upgrade/laravel-11
composer require laravel/framework:^11.0 --with-all-dependencies
composer require --dev phpunit/phpunit:^11.0 --with-all-dependencies

If Composer conflicts, identify blockers immediately:

composer why-not laravel/framework 11.0

Common culprits on apps I upgrade: older spatie/laravel-permission, abandoned UI scaffolding packages, and custom forks pinned to ^10.0. Update each package to its Laravel 11-compatible release before retrying.

Step 2: Adopt the Laravel 11 bootstrap structure

Compare your bootstrap/app.php against a fresh Laravel 11 skeleton. Middleware that lived in app/Http/Kernel.php moves into the bootstrap file:

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();

You can migrate incrementally — Laravel 11 does not force you to delete Kernel.php on day one if your middleware stack is complex. I've migrated legal-tech portals with fifteen custom middleware classes by aliasing them first, then pruning dead entries.

Step 3: Publish and reconcile config files

Laravel 11 ships slimmer defaults. If your app relies on explicit config values, publish only what you need:

php artisan config:publish caching
php artisan config:publish queue
php artisan config:publish mail

Diff published files against your Laravel 10 copies. Pay attention to config/database.php Redis options, config/queue.php retry_after values, and config/filesystems.php S3 disk settings — silent default changes have caused production upload failures on portals using Spatie Media Library.

Step 4: Update Eloquent casts and migrations

Laravel 11 encourages the casts() method on models instead of the $casts property. Both work during transition, but new code should use the method form for Laravel 12 alignment:

protected function casts(): array
{
    return [
        'published_at' => 'datetime',
        'meta' => 'array',
        'is_active' => 'boolean',
    ];
}

Laravel 11 also supports per-model migration paths via php artisan make:model -m conventions. Existing migrations remain valid — run php artisan migrate:status on staging before and after upgrade.

Step 5: Rebuild front-end tooling

Laravel 11 expects Vite. If your Laravel 10 app still uses Mix, migrate first — see Vite config for Laravel projects. After the framework bump:

rm -rf node_modules package-lock.json
npm install
npm run build

Commit compiled assets if your production server has no Node.js, which is the pattern I use with Deployer releases on shared EC2 infrastructure.

How do you upgrade Laravel 11 to Laravel 12 without breaking production?

Once Laravel 11 runs green in staging, the 11→12 hop is comparatively small. Laravel 12 focuses on maintenance, dependency updates, and new starter kits — most existing Laravel 11 applications require minimal code changes.

Bump the framework constraint

git checkout -b upgrade/laravel-12
composer require laravel/framework:^12.0 --with-all-dependencies
php artisan about

Review the Laravel 12 upgrade notes for removals affecting your app. As of 2026, watch for: UUID trait default changes, image validation rule updates, and Carbon 3.x as the default date library. If your app passes dates as strings without type hints, add explicit casting now.

Refresh first-party Laravel packages together

composer require laravel/sanctum:^4.0 laravel/tinker:^2.10
composer require laravel/horizon:^5.30 --with-all-dependencies
composer require livewire/livewire:^3.5 --with-all-dependencies

Version numbers shift — always check each package's README for the Laravel 12 compatible tag rather than guessing. Run composer audit after every bump; security advisories blocked on Laravel 10 often clear once you reach 12.

Run automated formatting and static analysis

After both hops, normalize code style before review:

vendor/bin/pint
vendor/bin/phpstan analyse

This catches import path changes and deprecated facade usage. See PHP coding standards with Laravel Pint for CI integration patterns.

Migration CI PipelineGit pushComposerinstallPHPUnit+ PHPStannpm buildDeploy to stagingManual smoketests pass?ProductionSmoke test checklist• Login / password reset• File upload + download• Payment callback (eSewa/Khalti)• Queue job + scheduled task
Run PHPUnit, static analysis, and staging smoke tests before each production release during the Laravel 12 migration

What should you test after a Laravel 12 migration?

Passing php artisan test is necessary but not sufficient. On a production Laravel application, I run a structured smoke matrix after every major upgrade.

  1. Authentication flows. Login, logout, password reset, email verification, two-factor if enabled. Sanctum token issuance for mobile or SPA clients.
  2. Authorization. Role and permission gates — Spatie Permission middleware aliases must still resolve after the Kernel migration.
  3. File and media handling. Upload, resize, S3/local disk reads. Legal-tech portals with document sharing break here when storage:link or disk config drifts.
  4. Payments and webhooks. eSewa, Khalti, Stripe, or PayPal callback URLs must return 200 on test transactions. Replay webhook payloads from logs.
  5. Queues and scheduler. Dispatch a test job to Redis; confirm Horizon dashboard loads. Run php artisan schedule:list and trigger one scheduled command manually.
  6. Mail and notifications. Send a test mailable through SES or SMTP; verify queue serialization did not break notification classes.
  7. API contracts. Hit critical REST endpoints with Postman or your existing collection; confirm pagination and error JSON shape unchanged.

Expand automated coverage before upgrading if manual testing currently carries the load. Even ten feature tests around checkout, booking, or document upload save hours during the 11→12 hop.

Post-Upgrade Failure Decision Tree500 error after deploy?Check storage/logs+ APP_DEBUG offAssets 404?Run npm run buildClass not found?composer dump-autoloadStale config?config:clear + cache:clearReload PHP-FPM + opcache
Common Laravel 12 migration gotchas — logs, assets, autoload, cache, and opcache stale code

How do you deploy Laravel 12 safely on a live server?

The deploy step is where upgrades that passed staging still fail — usually opcache serving old bytecode, wrong PHP-FPM socket, or a queue worker running the previous release. I use Deployer 7 with symlinked releases on several sister sites; the same principles apply to any zero-downtime workflow.

Pre-deploy checklist

  • Database backup verified and restorable ( mysqldump or managed snapshot ).
  • Maintenance mode plan — use php artisan down --secret="your-token" for bypass URL if needed.
  • PHP-FPM pool points to 8.2+ binary matching your CLI version.
  • Supervisor or systemd queue workers reference the current release path or restart on deploy.
  • Cron entry calls php artisan schedule:run with the correct absolute path.

Deploy commands sequence

php artisan down --retry=60
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
php artisan up
sudo systemctl reload php8.3-fpm
sudo supervisorctl restart all

After symlink swap with Deployer, reload PHP-FPM every time — opcache does not always pick up new files on busy pools. See zero-downtime deployment for Laravel with Deployer and GitLab CI/CD deploy to a VPS for pipeline templates that run tests before SSH deploy.

Post-deploy monitoring

Watch error rates for thirty minutes minimum. Check storage/logs/laravel.log, Horizon failed jobs, and slow query log if MySQL 8.0/8.4 shows new full table scans. Keep the previous release directory for quick rollback — dep rollback or manual symlink revert — until the business confirms critical flows work.

Zero-Downtime Release Layout/var/www/app/current → symlink (public web root)releases/12Laravel 10 (old)releases/13Laravel 11 stagingreleases/14Laravel 12 (new)Symlink swap + FPM reloadShared (persistent)• .env• storage/ (uploads, logs)• Redis sessions• MySQL 8.x database
Symlinked Deployer releases let you roll back a Laravel 12 migration without losing uploads or environment config

Budget realistically: a modest Laravel 10 app with good test coverage takes one to three days for 10→11 and half a day for 11→12. Apps with heavy custom packages, Livewire 2, or legacy Mix assets need a week. For teams without dedicated DevOps, hiring a Laravel developer in Nepal for the migration window often costs Rs 80,000–200,000 (~USD 600–1,500) — less than the revenue lost from a botched same-day production upgrade.

What is the fastest path through this Laravel 12 Real World Migration Guide from Laravel 10?

Work in this order: upgrade PHP on staging, fix deprecations while still on Laravel 10, jump to Laravel 11 and stabilize, then bump to Laravel 12. Never combine PHP, framework, and major package upgrades in a single commit. Keep staging mirroring production — same PHP minor, same Redis 7.x, same MySQL version — because database migration best practices only help when the environment matches.

If you are mid-upgrade and stuck, capture composer why-not output, the exact exception from storage/logs, and your php artisan about screen. That triplet resolves most blockers without a rewrite. Follow Laravel best practices as you land on 12 so the next major upgrade is cheaper.

Need hands-on help upgrading a live business app — booking portal, eCommerce store, or legal-tech platform — without downtime? Contact me with your current Laravel and PHP versions, and I will outline a migration plan scoped to your codebase.

Frequently Asked Questions

Laravel 12 requires PHP 8.2 or higher. PHP 8.3 is widely used in production; PHP 8.4 is the latest stable. Upgrade PHP on staging before touching application code.

Small apps: 1–3 days. Medium apps with queues, APIs, and third-party packages: 1–3 weeks. Large legacy codebases: 4–8 weeks. Budget Rs 80,000–400,000 (~USD 600–3,000) for freelance migration work in Nepal.

You can jump directly, but I recommend Laravel 10 to 11 first, then 11 to 12. Each major release has its own breaking changes, and splitting the work makes Composer conflicts, deprecated code, and test failures easier to isolate. On client projects I've migrated incrementally and hit far fewer surprises than attempting a single big-bang upgrade.

Expect middleware registration changes, updated default config files, stricter type hints, removal of deprecated helpers, and package incompatibilities. Laravel 11 slimmed the default skeleton; Laravel 12 continues that direction. Form Request validation, Eloquent casts, and queue job serialization often need attention. Run php artisan route:list and your full test suite after each step. I've seen apps break on custom service providers that relied on removed bootstrap patterns.

Clone production to a staging server running PHP 8.2+, create a git branch, and update composer.json constraints one major at a time. Run composer update, read the official upgrade guide for each version, and diff config files against a fresh Laravel install. Fix deprecations shown in logs, run phpunit or pest tests, then deploy via your normal pipeline. Never upgrade production first. I use Deployer 7 with a staging target before touching the live symlink.

Spatie packages, Sanctum, Passport, Livewire, Maatwebsite Excel, and custom private packages are frequent offenders. Run composer why-not laravel/framework 12.x to see blockers. Check each package's GitHub releases for Laravel 12 support before starting. On production apps I've had to temporarily fork packages or swap alternatives when maintainers lagged behind a new major. Lock working versions in composer.lock and commit it.

Not a full rewrite, but expect changes. Laravel 11 moved middleware to bootstrap/app.php instead of app/Http/Kernel.php in new skeletons. Existing Laravel 10 apps can keep Kernel.php during migration, though aligning with the new bootstrap style reduces future friction. Route files, API resources, and Form Requests mostly carry over. Audit custom middleware for signature changes and ensure route caching still works with php artisan route:cache after upgrade.

Your existing migrations should run unchanged on MySQL 8.0 or PostgreSQL 16. Do not rewrite old migration files unless a column type or index syntax changed. Run php artisan migrate on staging against a production database copy. Watch for Eloquent cast changes in Laravel 11+ that affect how JSON or datetime columns serialize. Back up before upgrading: mysqldump or pg_dump, plus a tested restore. I've avoided schema changes during framework upgrades unless strictly required.

Run your existing PHPUnit or Pest suite on PHP 8.2 before changing Laravel versions. After each major bump, re-run tests and manually hit auth, file uploads, queued jobs, scheduled tasks, and payment webhooks. Feature tests catch routing and middleware breaks; unit tests catch type and cast issues. Add smoke tests for critical paths if coverage is thin. On real client projects, I test eSewa and Khalti callback URLs on staging because gateway IP whitelists often block local environments.

Run composer dump-autoload -o, clear caches with php artisan optimize:clear, and confirm APP_ENV matches your .env. Namespace changes in moved classes are common after package updates. Check config/app.php aliases and any manual classmap entries. If opcache serves stale classes on production, reload PHP-FPM after deploy. I've fixed many post-upgrade 500 errors simply by deleting bootstrap/cache/*.php and redeploying with a fresh composer install --no-dev on the server.

Rebuild only if the codebase is unmaintainable, has no tests, and business logic is minimal. For working production apps with real users and data, incremental migration is almost always cheaper and safer. A rewrite sounds clean but usually takes 3–5 times longer and introduces new bugs. I've modernised legacy Laravel 6 apps incrementally through Laravel 12 without a full rewrite. Migrate when security support ends or packages you depend on drop Laravel 10 compatibility.

Confirm the server runs PHP 8.2+ with required extensions: mbstring, openssl, pdo, tokenizer, xml, ctype, json, bcmath, fileinfo. After symlink swap, run php artisan config:cache, route:cache, and view:cache in production. Reload PHP-FPM to flush opcache. Ensure cron still points to the current release path if you use Deployer 7; stale paths break scheduled tasks. Queue workers need supervisor restarts. Several sites I maintain on shared EC2 use this exact post-deploy sequence without downtime.

Laravel 12 inherits security patches and hardened defaults from versions 11 and 12, including improved validation, updated dependencies with CVE fixes, and better cookie and session handling. Running unsupported Laravel 10 versions means missing security releases. Upgrade PHP alongside the framework because PHP 8.1 and older versions no longer receive security updates. Enable APP_DEBUG=false in production, rotate APP_KEY only with a planned session invalidation strategy, and review CORS and Sanctum token settings after upgrade.

Classic causes: production still runs PHP 8.1, missing PHP extensions, wrong file permissions on storage/ and bootstrap/cache/, stale opcache, or .env differences. Case-sensitive Linux paths expose macOS typos. Run php -v and php -m on both environments and compare. Check Apache or Nginx error logs and storage/logs/laravel.log. I've fixed deploy failures caused by www-data lacking write access after a release swap, and by CI installing dev dependencies while production expected --no-dev.

Read the Laravel 11 upgrade guide first, then the Laravel 12 upgrade guide on laravel.com/docs. Each page lists breaking changes, removed features, and config diffs. Use laravel-shift.com for automated PRs if budget allows; Shift costs roughly USD 9–29 per jump. Cross-check with your installed packages' changelogs. Keep a rollback branch and database backup until production runs cleanly for at least one full business cycle including month-end or payment reconciliation jobs.

Share this article

What I've Built

Products I Build & Run

Legal-tech and language-services platforms I designed, built and operate — each running in production for real clients across Nepal and Australia.

More in the making — I keep shipping tools for Nepal's legal, language and digital work. Got an idea worth building?

Quick Contact Options
Choose how you want to connect me: