Drupal 12 is now close enough to require engineering preparation rather than casual forward planning. The current Drupal core schedule places the Drupal 12.0.0 release in the week beginning 7 December 2026, with the release-candidate window in the week beginning 9 November. Drupal 10 reaches end of life on 9 December 2026, making the next few weeks particularly important for teams still operating older installations or carrying untested Drupal 11 customisations. ([drupal.org](https://www.drupal.org/about/core/policies/core-release-cycles/schedule?utm_source=openai))
This is not a recommendation to upgrade a production site to an untested release candidate. It is a recommendation to make the upgrade boring before the final release arrives. The work is primarily dependency analysis, platform validation, code cleanup, test coverage and deployment rehearsal. If those activities begin only after Drupal 12.0.0 is released, the technical risk will be much higher than it needs to be.
What is changing for Drupal 12?
Drupal 12 raises the minimum PHP version to PHP 8.5. It also raises several database requirements, including MariaDB 10.11 and PostgreSQL 18, while retaining MySQL 8.0 and SQLite 3.45 as the documented minimums. Drupal 12 also requires Node.js 24 for core development workflows. ([drupal.org](https://www.drupal.org/about/core/blog/announcing-drupal-1200-platform-requirements?utm_source=openai))
The PHP change is the most immediate operational concern. A Drupal 11 site running PHP 8.3 or PHP 8.4 can remain on Drupal 11, but it cannot move directly to Drupal 12 until the runtime and all relevant extensions support PHP 8.5. This affects more than the web server. The CLI PHP binary used by Composer, Drush, PHPUnit and deployment jobs must also be compatible.
Drupal 12 also removes or changes parts of Drupal that have already been deprecated. The published core change records identify removals including the Search module, the Toolbar module, the Claro administration theme and the Olivero frontend theme. Those changes matter most for custom distributions, installation profiles, administrative customisations and projects that still assume these components are present in core. ([drupal.org](https://www.drupal.org/list-changes/drupal?utm_source=openai))
Drupal 12 is not an arbitrary jump into unknown territory. However, it is a major-version upgrade and should be treated as a compatibility project. Drupal’s documented upgrade path requires sites to move through supported major versions rather than skipping directly across multiple major versions. A Drupal 10 site should therefore move to Drupal 11 before targeting Drupal 12. ([drupal.org](https://www.drupal.org/docs/upgrading-drupal/upgrading-drupal?utm_source=openai))
Start with an inventory, not Composer
The most common upgrade mistake is to run a large Composer update before understanding what the site contains. Composer can tell you that a dependency cannot be installed, but it cannot tell you whether the dependency is business-critical, replaceable, abandoned or only present because it was once used by a development tool.
Begin by recording the current application, platform and deployment state:
php --version
composer --version
drush status
drush core:status
drush pm:list --type=module --status=enabled
drush pm:list --type=theme --status=enabled
drush config:status
Save the output as an artefact attached to the upgrade work. Also record the PHP extensions loaded in the production-like environment:
php -m
php --ini
composer show --direct
composer outdated --direct
The direct dependency list is especially useful. These are the packages your project explicitly requires, rather than packages pulled in transitively by Drupal core or another module. Review each entry and classify it as one of the following:
- required at runtime;
- required only for development or testing;
- required by a custom module or theme;
- temporary and safe to remove;
- unmaintained or awaiting a replacement.
Do not assume that a module with a stable release is ready for Drupal 12. Check its project page, release metadata, issue queue and compatibility declaration. Conversely, do not assume that a module without a Drupal 12-specific release is unusable: some projects declare compatibility with a range such as ^10 || ^11 || ^12, while others may need a small compatibility patch before their next tagged release.
Move the site to a clean Drupal 11 baseline
Drupal’s upgrade guidance specifically says that sites should be on Drupal 11.3.0 or higher before upgrading to Drupal 12. The reason is important: Drupal 12 removes update hooks added before Drupal 11.3. Updating first gives the current site the opportunity to run those older update paths while they are still available. ([drupal.org](https://www.drupal.org/node/3585783?utm_source=openai))
Before investigating Drupal 12 compatibility, bring the site to the latest supported Drupal 11 minor and patch release that your project can safely use. Do this on a branch and in a representative non-production environment:
git checkout -b chore/drupal-11-baseline
composer outdated "drupal/*" --direct
composer update "drupal/core-*" --with-all-dependencies
drush updatedb:status
drush updatedb
drush cache:rebuild
drush config:export
The precise set of packages updated by Composer depends on the project template and dependency constraints. Projects using drupal/core-recommended should continue to use the project’s established Composer structure rather than replacing it with an ad-hoc core package. Commit the resulting composer.lock, configuration export and any required code changes separately so that the baseline can be reviewed and reverted independently.
After updating, test the site as Drupal 11 rather than immediately treating a successful Composer run as evidence of readiness. Run database updates, review the status report, inspect logs and exercise the application’s most important workflows. A clean dependency update with a broken editorial workflow is still a failed upgrade.
Raise the PHP version in stages
PHP 8.5 is a hard requirement for Drupal 12. The safest approach is to test it before the major-version upgrade, while the site is still running Drupal 11. Drupal’s PHP requirements documentation lists PHP 8.5 as supported by Drupal 11.4 and Drupal 12, but not by the earlier Drupal 11 minor versions shown in the support matrix. Confirm the exact core version and platform combination used by the project before changing the runtime. ([drupal.org](https://www.drupal.org/docs/getting-started/system-requirements/php-requirements?utm_source=openai))
Build a PHP 8.5 environment that matches production as closely as possible. That includes:
- the same web server and PHP SAPI;
- the same database engine and version;
- the same PHP extensions;
- the same OPcache and realpath settings;
- the same filesystem permissions and private-file configuration;
- the same queue workers, cron jobs and command-line tooling.
Verify both the web request and CLI environments. It is surprisingly common for the browser to use one PHP binary while deployment jobs use another:
php --version
php -r 'echo PHP_SAPI, PHP_EOL;'
php -m | sort
drush status --fields=drupal-version,uri,db-driver,php-os,php-version
Pay particular attention to native extensions and third-party PHP libraries. A site can pass a basic homepage check while failing when an image is processed, a PDF is generated, an email is sent, an external API is called or a queue worker starts. Include those paths in the test plan rather than relying on a single browser smoke test.
Use Upgrade Status before the major upgrade
The Upgrade Status project is designed to identify compatibility problems before a major Drupal upgrade. Its current project documentation describes checks for the current Drupal version, next-major requirements, deprecated APIs, deprecated libraries, extension metadata and project awareness. The project also provides Drush and CI-oriented workflows. ([drupal.org](https://www.drupal.org/project/upgrade_status?utm_source=openai))
Install the version appropriate to the project and test it in a disposable branch. The project documentation currently identifies the 5.x alpha line as covering Drupal 10, 11 and 12, while the 4.x stable line covers Drupal 9, 10 and 11. Check the current release information before selecting a constraint; do not copy an old constraint from a previous upgrade project. ([drupal.org](https://www.drupal.org/project/upgrade_status?utm_source=openai))
composer require --dev 'drupal/upgrade_status:^5.0@alpha'
drush en upgrade_status -y
drush cr
Run the checks against the codebase before installing Drupal 12. Upgrade Status is most useful while deprecated APIs and older libraries are still present, because it can identify code that will disappear during the next major release. Its documentation explicitly warns that many checks are difficult or impossible to perform after the site has already been upgraded. ([drupal.org](https://www.drupal.org/project/upgrade_status?utm_source=openai))
Give every finding an owner and a disposition. “Fix later” is not a useful status. Use categories such as:
- must fix before Drupal 12;
- replace with a supported API;
- remove unused code;
- contrib project awaiting an upstream release;
- false positive, with a recorded explanation;
- accepted technical debt with an explicit deadline.
Search custom code for deprecated APIs
Automated scanners are valuable, but they do not replace a review of custom modules, themes, installation profiles and deployment scripts. Start with a targeted search for Drupal deprecations and APIs that are known to be sensitive during major upgrades:
grep -RIn \
--exclude-dir=.git \
--exclude-dir=vendor \
--exclude-dir=node_modules \
-E 'drupal_static|theme-settings\.php|hook_theme|entity_load|node_load|user_load' \
web/modules/custom web/themes/custom
This command is only a first pass. Some matches are legitimate compatibility wrappers, comments or test fixtures. Each result needs context. Replace deprecated procedural calls with the current service or entity APIs where appropriate, and make sure the replacement is tested for access checks, language handling, revisions and cacheability.
Pay special attention to custom code that builds render arrays, alters forms, defines services or implements plugins. Major-version changes often expose assumptions rather than obvious syntax errors. A custom block may still render but lose cache metadata; a form alter may still display but fail when an AJAX request is made; a service may instantiate successfully until a particular code path requests a changed dependency.
Run static analysis and the project’s test suite as part of the same branch:
vendor/bin/phpunit -c web/core/phpunit.xml.dist
vendor/bin/phpstan analyse web/modules/custom web/themes/custom
The exact PHPUnit and PHPStan configuration varies between Drupal projects, so use the project’s documented commands if they differ. The important principle is to make the checks repeatable and suitable for continuous integration rather than depending on a developer’s local environment.
Audit core components that are changing
Drupal 12 change records identify several core components that are removed or deprecated. The Search module, Toolbar module, Claro and Olivero are particularly relevant because they are often enabled by installation profiles or assumed by administrative customisations. ([drupal.org](https://www.drupal.org/list-changes/drupal?utm_source=openai))
Search
Determine whether the site uses Drupal core Search, a contributed search module, an external search platform or a custom query. Check content indexing, exposed filters, autocomplete widgets, access filtering and multilingual behaviour. If a contributed replacement is required, test indexing and deployment from an empty environment; search indexes are frequently treated as persistent infrastructure even though they may need to be rebuilt after an upgrade.
Toolbar
Review administrative navigation, permissions and custom toolbar alterations. A site may not visibly depend on the Toolbar module while a custom module still alters its render arrays or attaches libraries to its routes. Search custom code and configuration for toolbar-related references, then test administrator workflows using the actual permission sets used by editors and site managers.
Claro and Olivero
Do not leave administration or frontend theme selection to chance. Exported configuration can refer to themes that no longer exist in the codebase. Check the active theme, the default theme, administrative theme settings and any configuration split files. If the project uses a custom theme based on Claro or Olivero, separate inheritance assumptions from actual theme code and test the custom theme independently.
Prepare Composer for a controlled Drupal 12 test
Once the Drupal 11 baseline, PHP 8.5 environment and compatibility fixes are ready, create a dedicated Drupal 12 upgrade branch. Drupal’s official upgrade instructions use the core recommended, Composer scaffold and project message packages with a ^12 constraint, and recommend a dry run before making the actual update. ([drupal.org](https://www.drupal.org/docs/upgrading-drupal/upgrading-drupal/how-to-upgrade-from-drupal-11-to-drupal-12?utm_source=openai))
git checkout -b chore/drupal-12-readiness
composer require \
'drupal/core-recommended:^12' \
'drupal/core-composer-scaffold:^12' \
'drupal/core-project-message:^12' \
--no-update
composer require drush/drush --no-update
composer update --dry-run
Do not proceed simply because the dry run prints a solvable dependency graph. Review the proposed changes carefully. Look for:
- contributed modules being removed or downgraded;
- packages forced to development or alpha releases;
- unexpected PHP extension requirements;
- replacement packages introduced by a module;
- changes to Symfony, Twig, Guzzle or other shared libraries;
- custom packages whose constraints exclude PHP 8.5 or Drupal 12.
If the dry run is acceptable, perform the update with all dependencies, then run database updates and rebuild caches:
composer update "drupal/core-*" drush/drush --with-all-dependencies
composer install
drush updatedb:status
drush updatedb
drush cache:rebuild
drush config:status
Keep the Composer changes reviewable. Avoid combining the Drupal 12 core update, a large module replacement, unrelated feature work and a production configuration change in one commit. When the upgrade fails, a small series of purposeful commits makes diagnosis substantially easier.
Test deployment, configuration and content—not just PHP
A Drupal upgrade is successful only when the application can be built and operated repeatedly. Test the full deployment path from a clean checkout or a fresh environment. The test should install dependencies from composer.lock, apply configuration, run database updates, rebuild caches and produce the expected site.
composer install --no-interaction --prefer-dist
drush deploy
drush config:status
drush status
The deploy command is appropriate for projects that have adopted Drupal’s deployment workflow and configured the relevant deployment steps. If the project uses a bespoke deployment script, use that script instead and verify each step explicitly. Never introduce a deployment command into production merely because it appeared in an upgrade article; confirm that it matches the project’s Drush version and operational conventions.
Test configuration imports with the same checks used in production:
drush config:export --destination=/tmp/drupal-config
drush config:import --partial --source=/tmp/drupal-config
drush config:status
Be careful with partial imports. They can be useful for isolated tests, but they do not prove that a complete synchronisation is safe. The final rehearsal should use the project’s normal full configuration import process in a disposable environment restored from a production-like backup.
Build a meaningful regression matrix
At minimum, test the workflows that generate revenue, compliance obligations, editorial output or public trust. A useful Drupal regression matrix includes:
- anonymous and authenticated page delivery;
- editor login, content creation, moderation and publishing;
- media uploads, image styles and private files;
- translations and language negotiation;
- forms, CAPTCHA, email delivery and confirmation messages;
- search, filters, exposed Views and pagination;
- commerce, subscriptions or payment callbacks where applicable;
- cron, queues, scheduled publishing and webhooks;
- JSON:API, REST, GraphQL or other integration endpoints;
- cache tags, reverse proxy behaviour and CDN invalidation;
- backup, restore and rollback procedures.
For public pages, compare representative responses before and after the upgrade. Check status codes, canonical URLs, cache headers, structured data, asset loading and accessibility. For editorial pages, test actual user roles rather than using a superuser for every scenario. Permission changes and administrative theme changes often remain invisible when testing only as UID 1.
Security and governance considerations
Do not postpone security work until after the upgrade. Drupal’s security guidance emphasises keeping core and contributed projects up to date when advisories are issued, and the recent Drupal security programme has highlighted the importance of maintaining a complete workflow from vulnerability discovery through remediation. ([drupal.org](https://www.drupal.org/docs/administering-a-drupal-site/security-in-drupal/is-drupal-secure?utm_source=openai))
Use the upgrade project to improve operational controls:
- subscribe the team to Drupal security advisories;
- pin and review Composer dependencies through lock files;
- record the PHP and database versions used in each environment;
- scan dependencies in CI and review exceptions;
- restrict production database updates to the deployment process;
- take verified backups before database updates;
- test restoring both the database and files directory;
- document who can approve emergency security updates.
Do not treat a major-version upgrade as permission to disable security controls. Avoid turning off trusted host patterns, access checks, CSRF protection, file validation or caching simply to make a test pass. If a workaround is unavoidable, isolate it to the test environment, document it and create a removal issue before the production release.
A sensible release plan
For a production site, the final upgrade plan should answer five questions clearly:
- Which Drupal 11 version is the source baseline?
- Which PHP, database and web-server versions are used after the upgrade?
- Which contributed projects or custom components required changes?
- Which commands run during deployment, and in what order?
- How is the site restored if the application or database update fails?
A practical sequence is:
- Freeze feature development or establish a strict branch policy.
- Update Drupal 11 to a supported baseline.
- Complete PHP 8.5 and database compatibility testing.
- Resolve Upgrade Status and static-analysis findings.
- Upgrade the application in a clean test environment.
- Restore a recent production backup and repeat the upgrade.
- Run the regression matrix with realistic roles and integrations.
- Perform a timed deployment rehearsal.
- Deploy during a monitored maintenance window.
- Run smoke tests, review logs and monitor queues and integrations.
Keep the old production release available until the new release has passed its observation period. A Composer rollback alone may not reverse database updates, configuration changes or content transformations. The rollback strategy must therefore cover code, configuration, database and files, with an explicit decision about whether newly submitted content will be preserved or temporarily blocked.
Conclusion
The approaching Drupal 12 release is a useful forcing function for improving upgrade discipline. The important work is not the final Composer command; it is establishing a clean Drupal 11 baseline, testing PHP 8.5, identifying deprecated APIs, checking contributed-project readiness and proving that the complete deployment and rollback process works.
Teams that begin now can use the release-candidate period to find compatibility issues while the upgrade path is still being exercised by the community. Teams that wait until Drupal 12.0.0 is released will be competing with every unresolved module, platform constraint and undocumented deployment assumption at the same time. Treat the upgrade as a tested engineering change, not a package update, and Drupal 12 should be a controlled continuation of the site rather than a crisis project.
Further reading
The Drupal core release schedule, official Drupal 11-to-12 upgrade guide, PHP requirements documentation, core change records and Upgrade Status project documentation should be treated as the authoritative references for the project’s final upgrade decisions. Release candidates and stable releases can change the exact compatibility picture, so re-check those sources immediately before approving production deployment. ([drupal.org](https://www.drupal.org/about/core/policies/core-release-cycles/schedule?utm_source=openai))