practical walkthrough for site owners, developers and DevOps teams
If your website runs on Drupal 10, the clock is ticking. Drupal 10 reaches end of life on 9 December 2026, which means no more security advisories or fixes after that date. The good news: moving from Drupal 10 to Drupal 11 is one of the smoothest major upgrades in Drupal’s history. It is not a rebuild like the old Drupal 7 to 8 migration. It is an in-place upgrade done with Composer, provided you prepare properly.
This guide takes you from the basics (what to check and why) to advanced techniques (automated code fixes, CI pipelines and handling stubborn modules). Follow the steps in order, and you will arrive on Drupal 11 with minimal surprises.
Why Upgrade Now?
- Security: After 9 December 2026, Drupal 10 gets no more security coverage.
- Modern stack: Drupal 11 runs on newer PHP and Symfony components, with better performance and long-term support.
- Path to Drupal 12: Drupal 12 is scheduled for the week of 7 December 2026. You cannot jump straight from Drupal 10 to 12. You must pass through Drupal 11 (specifically 11.3 or higher).
- Smaller jump later: Staying current on Drupal 11 keeps future upgrades small and cheap.
Tip: Always confirm dates on the official Drupal core release schedule at drupal.org, because schedules can shift.
The Big Picture: Migration Roadmap
- Basic level: audit, backup, update to latest Drupal 10, check requirements.
- Intermediate level: compatibility checks for modules and themes, fix custom code, run the Composer upgrade.
- Advanced level: automate with Rector, CI pipelines, patches, performance and rollout strategy.
Part 1: Basic Level
Step 1: Take an Inventory of Your Site
Before touching any code, list what you have:
- Current Drupal core version (for example 10.4, 10.5 or 10.6)
- All contributed modules and themes, with versions
- All custom modules and custom themes
- Hosting environment: PHP version, database type and version, web server
- Integrations: search (Solr, Elasticsearch), CDN, SSO, payment, third-party APIs
You can get the module list quickly with Drush:
drush pm:list --status=enabled --type=module
composer show -i
This inventory becomes your migration checklist.
Step 2: Back Up Everything
Never upgrade without a restore plan.
# Database
drush sql:dump --gzip --result-file=../backup/pre-d11-$(date +%F).sql
# Files
tar -czf ../backup/files-$(date +%F).tar.gz web/sites/default/files
Also commit your composer.json, composer.lock and configuration (config/sync) to Git so you can roll back code instantly.
Step 3: Create a Safe Staging Environment
Do not upgrade on production first. Clone production (database plus files) to a staging or local environment. Tools like DDEV, Lando or Docker make this easy:
ddev config --project-type=drupal --docroot=web
ddev start
ddev import-db --file=backup.sql.gz
ddev import-files --source=files.tar.gz
Step 4: Update to the Latest Drupal 10 Release
First, get fully current on Drupal 10 (ideally the final 10.6.x release with all security fixes). This removes many deprecation problems before you even start.
composer update "drupal/core-*" --with-all-dependencies
drush updatedb -y
drush cache:rebuild
Fix any errors here first. If the site is unstable on Drupal 10, it will be worse on Drupal 11.
Step 5: Check Platform Requirements
Drupal 11 needs a more modern server than Drupal 10. Check these:
| Component | Drupal 11 requirement |
|---|---|
| PHP | 8.3 or higher (newer minor releases of Drupal 11 may raise this floor, so check the requirements page for your target version) |
| Composer | 2.7.0 or newer |
| Database | MySQL 8.0, MariaDB 10.6, PostgreSQL 16 or SQLite 3.45 (or newer) |
| Drush | Drush 13 or newer |
If your host runs PHP 8.1 or 8.2, upgrade PHP first and confirm that Drupal 10 still works on it. Doing this as a separate step makes problems much easier to diagnose.
Part 2: Intermediate Level
Step 6: Install and Run Upgrade Status
The Upgrade Status module is the single most useful tool for this migration. It scans your core, contrib and custom code and reports what is incompatible with Drupal 11.
composer require --dev drupal/upgrade_status
drush en upgrade_status -y
Then open Reports → Upgrade status in the admin UI (or use Drush):
drush upgrade_status:analyze --all
The report shows, per project, whether it is ready for Drupal 11, which deprecated APIs it uses, and whether an info.yml update is needed.
Step 7: Check Contributed Module and Theme Compatibility
For each contrib project flagged by Upgrade Status:
- Already compatible: update to the release that supports Drupal 11. Check the project page on drupal.org for the
^10 || ^11core constraint. - Not compatible yet, but a patch exists: look in the project’s issue queue for a Drupal 11 compatibility issue. Many projects have a ready patch or an automated merge request from the Project Update Bot.
- Abandoned or unmaintained: find a replacement, move the functionality into custom code, or sponsor the maintainer.
Also note that some modules and themes that used to be in core have moved to contrib. If you use one (for example older base themes), install the contrib version before upgrading core.
Step 8: Update Your Custom Code
Custom modules and themes need the most attention. Typical changes:
- Replace deprecated functions and classes with their modern equivalents.
- Update
*.info.ymlso it declares Drupal 11 support:
name: My Custom Module
type: module
core_version_requirement: ^10.3 || ^11
- Check your Twig templates, libraries and JavaScript for removed or changed behaviour.
- Make sure tests pass on the new version.
The golden rule: code that runs on the latest Drupal 10 without any deprecation warnings will almost always run on Drupal 11. Deprecated code in 10.x is what gets removed in 11.
Step 9: Make Your Composer Setup Ready
Open composer.json and loosen the core constraints so Composer can resolve Drupal 11:
"require": {
"drupal/core-recommended": "^11",
"drupal/core-composer-scaffold": "^11",
"drupal/core-project-message": "^11",
"drush/drush": "^13"
}
If you use drupal/core-dev, update that to ^11 as well.
Step 10: Run the Upgrade
Work on a Git branch in staging:
git checkout -b upgrade-drupal-11
composer require "drupal/core-recommended:^11" \
"drupal/core-composer-scaffold:^11" \
"drupal/core-project-message:^11" \
--update-with-all-dependencies
drush updatedb -y
drush config:import -y # only if you manage config in code
drush cache:rebuild
If Composer reports conflicts, read the message carefully. It usually names the exact module that does not yet allow Drupal 11. Update that module (or temporarily remove it) and retry.
Step 11: Test Thoroughly
Use a checklist covering:
- Home page, key landing pages and all content types (create, edit, delete)
- Views, blocks, menus and layout builder pages
- User login, registration, password reset and permissions
- Forms (Webform, contact forms) and email delivery
- Search, media library and file uploads
- Multilingual content and translations, if used
- Admin pages: Reports → Status report and Recent log messages (look for PHP errors and warnings)
- Front-end appearance on desktop and mobile browsers
Step 12: Deploy to Production
- Announce a maintenance window.
- Take a fresh backup of production.
- Enable maintenance mode:
drush state:set system.maintenance_mode 1. - Deploy the code (Git pull or your CI/CD deploy).
- Run
composer install --no-dev --optimize-autoloader. - Run
drush updatedb -y,drush config:import -yanddrush cache:rebuild. - Disable maintenance mode and run a quick smoke test.
- Monitor logs for the next 24 to 48 hours.
Part 3: Advanced Level
Step 13: Automate Code Fixes with Drupal Rector
Fixing deprecations by hand is slow. Drupal Rector applies many fixes automatically.
composer require --dev palantirnet/drupal-rector
cp vendor/palantirnet/drupal-rector/rector.php .
Edit rector.php to point at your custom code, then do a dry run first:
vendor/bin/rector process web/modules/custom --dry-run
vendor/bin/rector process web/modules/custom
Always review the resulting diff. Rector is excellent for repetitive API changes, but it does not replace testing.
Step 14: Static Analysis with PHPStan
Add phpstan-drupal and phpstan-deprecation-rules to catch deprecated calls and type errors:
composer require --dev phpstan/phpstan mglaman/phpstan-drupal phpstan/phpstan-deprecation-rules
Example phpstan.neon:
includes:
- vendor/mglaman/phpstan-drupal/extension.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
parameters:
level: 2
paths:
- web/modules/custom
- web/themes/custom
Run it in CI so new deprecations never slip in.
Step 15: Use the Lenient Plugin Carefully
Sometimes a contrib module works fine on Drupal 11 but has not yet declared compatibility, which blocks Composer. The Composer Drupal Lenient plugin can override the core constraint for specific packages:
composer require mglaman/composer-drupal-lenient
composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/some_module"]'
Use this only as a temporary bridge, and only after testing the module yourself. Track the module’s issue queue and remove the override as soon as an official release lands.
Step 16: Manage Patches Properly
Use cweagans/composer-patches so every patch is versioned and reproducible:
"extra": {
"patches": {
"drupal/some_module": {
"Drupal 11 compatibility": "patches/some_module-d11.patch"
}
}
}
Keep patch files in your repository and reference the drupal.org issue number in the description, so you can drop the patch once it is merged upstream.
Step 17: Build a CI/CD Pipeline for the Upgrade
A pipeline turns a risky one-off event into a repeatable process. A typical GitHub Actions workflow runs on every pull request:
name: Drupal 11 checks
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: composer:v2
- run: composer install --no-progress
- run: vendor/bin/phpstan analyse
- run: vendor/bin/phpunit web/modules/custom
- run: vendor/bin/phpcs --standard=Drupal,DrupalPractice web/modules/custom
Add automated visual regression (for example BackstopJS or Percy) and functional tests (Behat, Cypress or Playwright) for your most important user journeys.
Step 18: Handle Front-End and Theme Changes
- If your theme inherits from old base themes that were removed from core, switch to a maintained base theme or copy the templates and assets into your own theme.
- Review any custom JavaScript that relies on jQuery UI or other libraries that core has been phasing out. Replace them with vanilla JavaScript or a maintained library.
- Rebuild front-end assets (npm, Sass, Webpack or Vite) and test with the Olivero and Claro themes for comparison.
- Confirm that your WYSIWYG setup works with CKEditor 5 and any custom plugins you rely on.
Step 19: Plan for Performance and Observability
After the upgrade, compare before and after:
- Page response times (New Relic, Blackfire or Datadog)
- Cache hit ratios (Redis or Memcached, Varnish, CDN)
- Database query counts on heavy views
- Error rate in logs (Sentry, ELK or syslog)
Take advantage of what Drupal 11 offers, such as the newer object-oriented hook implementations and recipes, as you refactor over time. You do not have to adopt everything on day one.
Step 20: Use a Safe Rollout Strategy
For high-traffic or mission-critical sites:
- Blue-green deployment: run the upgraded site in parallel, then switch traffic once validated.
- Canary release: send a small percentage of traffic to the new version first.
- Rollback plan: keep the previous release and database snapshot ready. Know exactly how long a restore takes before you need it.
- Database changes: because
drush updatedbchanges the schema, a code-only rollback is not enough. Always restore the matching database backup.
Step 21: Think Ahead to Drupal 12
Drupal 12 requires a newer PHP version (currently planned as PHP 8.5) and drops some older modules. To stay ready:
- Land on Drupal 11.3 or higher, ideally the newest 11.x release, since sites below 11.3 cannot upgrade directly to Drupal 12.
- Keep running Upgrade Status regularly, since it also checks Drupal 12 readiness.
- Fix deprecation warnings as they appear in each Drupal 11 minor release.
Common Problems and Fixes
| Problem | Likely cause | Fix |
|---|---|---|
| Composer cannot resolve dependencies | A module does not allow Drupal 11 | Update it, patch it, or use the lenient plugin temporarily |
| White screen after upgrade | PHP version too low or fatal error in custom code | Check PHP version and web server error log |
| Missing theme styling | Base theme removed from core | Install a replacement base theme or copy assets |
drush command fails | Old Drush version | Upgrade to Drush 13 |
| Config import errors | Config for removed modules still present | Uninstall the module on Drupal 10 before upgrading |
Final Pre-Launch Checklist
- Full backup of files and database
- Staging site upgraded and tested
- PHP, database and Composer versions meet requirements
- All contrib modules updated to Drupal 11-compatible releases
- Custom code free of deprecation warnings
- Automated tests passing in CI
- Rollback plan documented and rehearsed
- Monitoring and alerts active after launch
Conclusion
Migrating from Drupal 10 to Drupal 11 is far less painful than past major upgrades, but it still rewards careful preparation. Start with an inventory and backups, get current on Drupal 10, use Upgrade Status to find problems, fix custom code (with Rector doing the heavy lifting), and rehearse the whole thing on staging before going live. With CI checks and a solid rollout plan, you will not only meet the December 2026 deadline but also be well positioned for Drupal 12 and beyond.
Need help? Contact a Drupal agency or your hosting provider’s support team, and always cross-check version requirements on drupal.org before you start.