Skip to main content

Upgrading from Drupal 10.6.x to 11.3.x is officially supported, but in real projects it’s rarely a single command. The friction usually comes from **Composer constraints**, not Drupal itself.

This article documents a real-world upgrade path from Drupal 10.6.1 → 11.3.2, including the specific blockers encountered and the sequence of steps that ultimately worked.

The environment:

  • PHP 8.4
  • Composer 2.9
  • DDEV
  • A site with non-trivial contrib and admin tooling (Gin, vendor hardening, core dev tools)

 

1. Pre-flight checks

Before starting:

  • Upgrade Status module shows no blocking issues
  • Database backup completed
  • PHP version meets Drupal 11 requirements
  • Composer is recent (2.6+)

At this point the site is theoretically ready, but Composer still needs help.

 

2. Upgrade contrib modules that block core changes

Color module

The Color module needed to move to a newer release line before core constraints could change cleanly.

composer require 'drupal/color:^2.0@alpha'

This avoids downstream conflicts when core updates its dependency graph.

 

3. Prepare Drupal 11 core constraints (without solving yet)

Rather than letting Composer immediately attempt a full dependency solve, the upgrade was staged by updating constraints only using `--no-update`.

composer require \
 drupal/core-recommended:^11.3 \
 drupal/core-composer-scaffold:^11.3 \
 drupal/core-project-message:^11.3 \
 --no-update

This updates `composer.json` without touching `composer.lock`, allowing other blockers to be resolved first.

 

4. Remove Gin (temporary but necessary)

Gin and Gin Toolbar were pinned to `<11.2`, which **hard-blocked Drupal 11.3**. Rather than forcing an intermediate Drupal version, they were temporarily removed.

composer remove drupal/gin drupal/gin_toolbar --no-update

Core constraints were then re-applied (defensive, but harmless):

composer require \
 drupal/core-recommended:^11.3 \
 drupal/core-composer-scaffold:^11.3 \
 drupal/core-project-message:^11.3 \
 --no-update

 

5. Fix hidden blockers: core dev tooling and vendor hardening

Core dev tools

The project was still pinned to Drupal 10 dev tooling, which blocks Symfony 7 upgrades.

composer require drupal/core-dev:^11.3 --dev --no-update

Vendor hardening

`core-vendor-hardening` also needed to move off the Drupal 10 line.

composer require drupal/core-vendor-hardening:^11 --no-update

(After the full update, this was later tightened to `^11.3`.)

 

6. Run the actual dependency solve

With all blockers removed or upgraded, Composer was finally allowed to do a full resolution:

composer update -W

This step:

  • Upgraded Drupal core to 11.3.2
  • Moved Symfony from 6.4 → 7.4
  • Updated all compatible dependencies in one pass

 

7. Finalise vendor hardening alignment

After the successful update, vendor hardening was aligned exactly with the core version:

composer require drupal/core-vendor-hardening:^11.3 --no-update

(No further solve was required.)

 

8. Run Drupal updates

Standard Drupal post-upgrade steps:

drush updatedb -y
drush cr
drush cex -y

At this point the site was fully running on Drupal 11.3.2.

 

9. Re-install Gin (Drupal 11 compatible versions)

Once core was stable, Gin and Gin Toolbar were reinstalled using versions compatible with Drupal 11.3:

ddev composer require drupal/gin:^5 drupal/gin_toolbar:^3 -W

Admin UI restored, without blocking core.

## Key lessons from this upgrade

  • You do not need to upgrade to 11.1 first  direct 10.6 → 11.3 is valid.
  • Most failures are caused by:
    • `core-dev:^10`
    • `core-vendor-hardening:^10`
    • Admin themes (Gin) with `<11.2` caps
  • Use `--no-update` aggressively to control Composer’s solver.
  • Remove blockers temporarily rather than forcing partial upgrades.
  • Let one `composer update -W` do the heavy lifting.

 

Drupal 11 itself is not the hard part Composer hygiene is. Once the project’s constraints accurately reflect Drupal 11 expectations, the upgrade becomes predictable and repeatable.

This sequence has now been proven end-to-end and can be reused safely on similar Drupal 10.6 projects.

Related articles

Andrew Fletcher04 Apr 2025
Managing .gitignore changes
When working with Git, the .gitignore file plays a critical role in controlling which files and folders are tracked by version control. Yet, many developers are unsure when changes to .gitignore take effect and how to manage files that are already being tracked. This uncertainty can lead to...
Andrew Fletcher26 Mar 2025
How to fix the ‘Undefined function t’ error in Drupal 10 or 11 code
Upgrading to Drupal 10.4+ you might have noticed a warning in their code editor stating “Undefined function ‘t’”. While Drupal’s `t()` function remains valid in procedural code, some language analysis tools — such as Intelephense — do not automatically recognise Drupal’s global functions. This...
Andrew Fletcher17 Mar 2025
Upgrading to PHP 8.4 challenges with Drupal contrib modules
The upgrade from PHP 8.3.14 to PHP 8.4.4 presents challenges for Drupal 10.4 websites, particularly when dealing with contributed modules. While Drupal core operates seamlessly, various contrib modules have not yet been updated to accommodate changes introduced in PHP 8.4.x. This has resulted in...