Drupal 9: A Practical Upgrade Path for Legacy Sites

Illustrated infographic summarizing: Drupal 9: A Practical Upgrade Path for Legacy Sites

By Greg Nowak. Last updated 2026-07-20.

A Drupal 9 site still running in 2026 is not simply behind on updates. Drupal 9 has received no official releases since 1 November 2023, so the site should be treated as a managed business risk. That does not automatically justify a rushed rebuild, but it does rule out ordinary maintenance as a sensible long-term plan.

The destination has changed too. As of 20 July 2026, Drupal 10 is supported only until 9 December 2026. For a viable legacy site, Drupal 10 is now a controlled bridge to Drupal 11—not a destination worth planning around.

Audit the estate before estimating the upgrade

The core update is rarely the hardest part of an inherited Drupal project. Uncertainty usually sits in custom modules, abandoned contributed packages, Composer patches, edited scaffold files, integrations, hosting constraints, and deployment steps that exist only in somebody’s memory.

Run the first checks in a restorable lower environment, from the directory containing composer.json:

composer show drupal/core | grep versions
composer show drush/drush
composer outdated 'drupal/*'
vendor/bin/drush status
vendor/bin/drush pm:list --status=enabled

Then inspect composer.json, the patches directory, custom modules and themes, CI configuration, cron, queues, search, email delivery, forms, payment or CRM connections, and any production-only configuration. Confirm that the repository can actually reproduce the live site. If it cannot, fixing that is part of the project—not an optional preliminary task.

Choose the route from evidence

Estate condition Likely route What must be proved first
Current content model, useful workflows, maintained modules Sequential upgrade to Drupal 11 Custom code and dependencies can cross both major-version boundaries
Valuable site, but weak deployment or documentation Stabilise, document, then upgrade A reproducible build, tested backup restoration and a credible regression plan
Abandoned modules, heavy production edits, obsolete workflows Compare upgrade with replatforming The cost of preserving the old architecture is lower than replacing it
Temporary or low-value site near retirement Contain risk and plan closure A named owner accepts the security and continuity exposure
A useful recommendation depends on technical condition and business value, not Drupal’s version number alone.

Use the supported stepping stones

Drupal’s upgrade path does not support jumping directly from Drupal 9 to Drupal 11 or 12. The practical sequence is:

  1. Prepare Drupal 9. Bring the site to Drupal 9.4 or 9.5—preferably 9.5 for this temporary stage—and make contributed and custom code Drupal 10-compatible. Replace removed Drupal 10 dependencies such as the Seven administration theme and CKEditor 4, or deliberately adopt their supported alternatives.
  2. Land on Drupal 10. Treat this as a tested release, not an invisible Composer operation. Recheck editor journeys, integrations, background jobs and configuration. Document and reapply intentional changes to scaffold files such as .htaccess.
  3. Prepare Drupal 11. Drupal 11 requires a source site on Drupal 10.3 or later and PHP 8.3 or later. Resolve extensions removed from Drupal 11, update custom code, then perform and test the second major upgrade.

Each stage should have its own clean commit, database backup, deployment record and acceptance result. Combining both upgrades into one unreviewable change set makes fault-finding and rollback unnecessarily difficult.

Turn compatibility assumptions into a blocker list

Upgrade Status remains useful because its stable 4.3 branch supports Drupal 9, 10 and 11. Install it on a development copy or short-lived assessment branch, following the project’s current dependency instructions:

composer require 'drupal/upgrade_status:^4.3'
vendor/bin/drush en upgrade_status -y
vendor/bin/drush upgrade_status:analyze --all

Use the report to separate three kinds of work: contributed packages that only need a supported release, custom code using removed APIs, and dependencies with no credible forward path. Upgrade Status can identify many compatibility problems, but it does not replace manual testing or a review of operational dependencies.

Keep Drush project-local and Composer-managed. Do not add the latest Drush to Drupal 9 without checking compatibility: current recommended Drush branches target newer Drupal versions, while older compatible branches are no longer supported. Pin the branch appropriate to each upgrade stage and run it through vendor/bin/drush so developer machines and CI use the same tool.

Plan acceptance around the work people actually do

A successful homepage smoke test proves very little. Before approving a release, test content creation and moderation, media uploads, permissions, search, forms and notifications, scheduled publishing, redirects, multilingual pages, API consumers, cron, queues and integrations. Include operations and editorial users early enough to catch changed workflows before the release window.

After each stage, useful checks include:

vendor/bin/drush updatedb:status
vendor/bin/drush updatedb -y
vendor/bin/drush config:export -y
vendor/bin/drush cron
vendor/bin/drush watchdog:tail

Also run the project’s automated tests and verify that a fresh composer install succeeds from the committed lock file. The release plan should name the decision-maker, acceptance criteria, rollback trigger and maximum acceptable outage—not merely list technical commands.

What a useful assessment should deliver

The best first deliverable is a short, prioritised plan: immediate exposure, upgrade blockers, the Drupal 10 bridge, the Drupal 11 destination, testing scope, and an honest upgrade-versus-rebuild recommendation. If your inherited site needs that clarity, Greg can help shape the audit into a delivery plan your business and technical teams can run.

Related on GrN.dk

Need help with this kind of work?

Plan your Drupal upgrade Get in touch with Greg.

Sources

Latest articles

An Apache version below 2.4.68 may still be patched. Package provenance, vendor advisories, module checks and runtime evidence reveal the real position.

PHP 8.2 security support ends on December 31, 2026. Here is how to audit, test, and migrate a mixed CMS estate without rushing production changes.

How Danish businesses can automate Gmail and Microsoft 365 with rapid sorting, limited permissions and human approval.

When WordPress jobs run late, check WP-Cron and queue capacity first. Diagnose triggers, handlers, and Action Scheduler without guesswork.

WordPress 7.1 makes speculative loading configurable. Here’s how to spot overlapping rules and test speed gains without adding hidden costs.

Multiple records for the same customer in HubSpot? Learn how CVR number matching, AI suggestions and human approval can help you clean up duplicates while keeping track of fields, associations and customer history.

Before a Google AI shopping pilot, check which products qualify, where your catalog data disagrees, and whether checkout reflects your delivery and return terms.

Check whether prompt caching reduces cost per completed task, accounting for cache writes, retries, review effort and the charges on your provider's bill.

A practical Drupal translation workflow for Danish service pages: German review, commercial approval, publication and keeping translations current after edits.

Build a weekly marketing report from GA4 and Google Ads with verified calculations, clear data caveats and a short AI draft to support your Monday meeting.