Skip to content
CH SCShop classdrupal · advanced · ~180 min · 6 steps

How to upgrade Drupal to the next major version

Take a Drupal site through a major version jump — readiness scan, contrib check, the upgrade itself — without discovering the blockers mid-leap.

August 10, 2026 · by Dane Petersen

A major version jump — Drupal 10 to 11 today, 11 to 12 when it lands in December 2026 — is not a big security update. It’s a portage: you carry everything you own across before the water opens back up. The good news is that almost all the work happens before the jump, while your site still runs fine. You’ll need a local copy in DDEV, comfort with the normal update routine, and a real afternoon. Note the deadline that’s driving many of these: Drupal 10 reaches end of life on December 9, 2026.

Run the readiness scan

The Upgrade Status module reads your whole codebase and tells you what would break on the next major. Install it on your local copy:

composer require --dev drupal/upgrade_status
drush en upgrade_status -y

Then visit Reports → Upgrade status. It checks the environment too — Drupal 11 wants PHP 8.3 or newer, so if your host is still serving 8.1, that conversation starts now, not on upgrade day.

Check every contrib module’s story

Every module on the report has one of three stories: a compatible release exists (update it), a compatible release is coming (watch the issue queue), or the project is abandoned (find a replacement or uninstall). Also check the report for core modules that got removed in the new major — Drupal 11 dropped Book, Forum, Statistics, Tour, Actions UI, and Activity Tracker. Each needs its contrib version installed, or a decision to let it go, before the jump. This step is the whole game; the composer command later just enforces whatever you decided here.

Fix deprecations while still on the old version

Custom modules and themes accumulate calls to APIs the new major removed. Upgrade Status lists them; Drupal Rector fixes most automatically:

composer require --dev palantirnet/drupal-rector
cp vendor/palantirnet/drupal-rector/rector.php .
vendor/bin/rector process web/modules/custom

Review the diff, run the site, commit. Because deprecated APIs still work on the old major, you can ship these fixes to live now and shrink the risky part of the jump to nearly nothing.

Do the jump on your local copy

Back up the local database first, then ask composer for the new core:

drush sql:dump --gzip --result-file=../backups/pre-major-$(date +%Y-%m-%d).sql
composer require 'drupal/core-recommended:^11' 'drupal/core-composer-scaffold:^11' --update-with-all-dependencies

(Adjust ^11 to whatever major you’re jumping to, and include drupal/core-dev if you have it.) If composer refuses, that’s a feature — the error names exactly which package is holding the door shut.

Work the error list to zero

Composer refusals mean a contrib module still lacks a compatible release: update it, replace it, or remove it, then re-run the require. Once composer succeeds, finish the database side:

drush updatedb -y
drush cache:rebuild

Loop until both commands run clean. Two or three loops is normal on a site with real history; it’s not a sign you did anything wrong.

Test, then repeat the jump on staging and live

Click through the front page, log in, edit content, run a checkout if you sell things. Then commit composer.json and composer.lock, deploy to staging, and run drush updatedb -y && drush cache:rebuild there — live gets the exact code your local copy just proved, never a fresh resolve. You’ll know you’re done when drush status shows the new major on live, the status report at Reports → Status report has no new errors, and the money paths click through the same as they did this morning.

That's the lesson. Back to the shop for more — or if this is the chore your organization never gets to,that's literally what we're for.