Pain free Drupal 8 to Drupal 9 upgrades
Backup EVERYTHING & work locally!
Even after 20 years of web development I still never ever "chance" large code or database updates and skip backing up all the code & database, that one time you don't will be the one time you regret it, even when working locally I take regular database backups of the site I'm working so I can roll back if needed.
Also goes without saying really but never attempt any code or database updates without testing them thoroughly locally first & then ideally on a dev server with the same spec as your production server or by using Docker or DDEV to replicate your production environment locally. My mac hates Docker so I only use it if a project 100% requires it!
Sync up your Drupal 8 config!
Hopefully your site configuration export is up to date with your live configurations if it's not export all your site config with via the config admin UI (/admin/config/development/configuration/full/export) or using
drush cex then test that you can import that same config to get your live & export configurations in sync.
The reason I suggest this I found it somewhat buggy on some sites to try to import Drupal 8 exported config into a Drupal 9 site (this may be a known thing but I didn't have time to read up why) so if you get your config all in sync first before you upgrade you know everything is up to date and can perform a config export straight after you do the Drupal 9 upgrade.
Get your Drupal 8 in order!
Before you even start to run Composer commands or download Drupal 9 code the absolute most important thing you need to do is get your Drupal 8 site version & config up to date and in order. How in order your Drupal 8 site is will determine how easily your Drupal 9 update will go. On two sites I upgrade the sites had got quite out of date and therefore took more work in this part to get them ready for Drupal 9. The other two sites had had regular updates and were already in good order so the updates were relatively pain free!
The first place you'll want to head to is /admin/reports/updates ("Available updates") and see how up to date your current Drupal 8 site actually is and how compatible is already is with Drupal 9.
Since we've reached Drupal 8 end of life you should find most your modules already have Drupal 9 compatible versions but you might not be running them yet so the first thing you should do is upgrade all your modules to the Drupal 9 (& Drupal 8) compatible versions.
To do this go to /admin/reports/updates and next to each module which has an update you'll see a "Compatible" link which you can click to see the compatibility of the release in question.
You're ideally looking for releases that are compatible with both Drupal 8 & 9 so it might look something like this:
Requires Drupal core: 8.9.20 to 9.3.2
Some modules can have multiple release updates and I would recommend at this stage just upgrading minor versions vs major versions ie. If you have module version
3.1 and there's 2 updates:
4.0, I found it less likely to cause issues by taking version
3.1.2 for now.
You can upgrade the major version later on once you have your site running smoothly on Drupal 9.
If you find a module that has Drupal 8 & Drupal 9 compatibility is separate module versions then just upgrade to the highest version for Drupal 8 for now since the Drupal 9 version would not run on the Drupal 8 codebase, we'll tackle these later with Composer.
From here you can go ahead and start to upgrade (either manually or better still with Composer - more on that in a moment) all your modules to versions compatible with both Drupal 8 & Drupal 9 and then update to the latest version of Drupal 8.9.x core. If you're already running Drupal core 8.8.x or 8.9.x then you can skip upgrading Drupal core but I would recommend taking your site to the latest Drupal 8 version regardless.
If you are running Drupal 8.7.x or below I recommend checking out the office Drupal.org notes.
Once all your Drupal code is up to date, clear the Drupal caches and then run the database updates via update.php or
drush updatedb. Depending how up to date your site was these updates might take awhile and I actually had to run the updates multiple times on one site.
Using Composer to update code
You can of course download all the updates manually but Drupal recommends maintaining site using Composer nowerdays and once you work it all out you'll understand why. With Composer you can update all your modules and libraries using command line & have it check all the dependencies for you which is the long & short term is a lot more efficient.
For the context of this post that's about all we'll touch on with Composer but it has many features & addons allowing for example easier patching, running scripts on installs and updates and a lot more. To learn more about Composer & Drupal checkout the official Drupal.org Composer documentation.
Anyway for this post we'll be using
composer require PROJECTNAME:PROJECTVERSION which will download our projects & modules to our local codebase and keep track of the current version for us (in the composer.lock file). You can require multiple projects in one go too like so:
composer require PROJECT1NAME:PROJECT1VERSION PROJECT2NAME:PROJECT2VERSION PROJECT3NAME:PROJECT3VERSION.......
PROJECTNAME for Durpal modules & themes is always
You can run this command without the PROJECTVERSION and have Composer determine the right version for you but when upgrading you might run into conflicts or require a specific module version so for my upgrades I was inputting the project version myself.
Here's an example where we'll update Admin Toolbar to latest 3.0.x version & cTools to the latest 3.7.x version:
composer require 'drupal/admin_toolbar:^3.0' 'drupal/ctools:^3.7'
Note the use of "
^", this is telling Composer "at least" the specified version, if you omit this you'll lock the project at a specific version which will very likely cause you conflicts when upgrading in the future so always remember the "
^" unless you absolutely need a specific version of a module for patching or similar.
And that's about all we'll need, I use Composer nearly everyday but I'm still learning and find new tips & tricks all the time.
What if my site doesn't use Composer?
Two of the sites I upgraded had been setup with Composer but then had been updated by another developer manually so modules had been added outside of Composer so when I ran
composer install to get all the packages & modules, half the codebase was missing and the site broke. I could have gone through the module page and added the missing modules to Composer but that's would have been mind numbing so I Googled to see if there was an automated way of doing this, and indeed"there's a module for that", called composerize! It's a simple module with a single admin page which generates a
composer.json file for your project based on the code/modules you have installed on your Drupal site. The
composer.json file wasn't prefect so I had to tweak it a bit but it saved me having to manually add all the modules to Composer so I was happy.
I then added the composer-installers-extender plugin so I could define where my modules & libraries should be downloaded to in my Drupal install since the site structure was a bit messy then I ran
composer install and all the modules for the sites where downloaded by Composer and I could move on with the rest of the upgrade!
I also found a Composer plugin called composerize-drupal which claims to do the same job but I haven't had a chance to test it yet.
Upgrade Status module & upgrading custom code
At this point after reviewing the available updates & upgrading modules you should have a pretty good overview of how compatible your Drupal 8 site will be with Drupal 9 but one thing the available updates page does not tell us is how compatible any custom code is. You will quite likely have at least a custom Drupal theme and perhaps a few custom modules which will also need upgrading to work with Drupal 9.
Fortunately, you guessed it again "There's a module for that" well a few things actually! The most essential is a module called Upgrade Status, you install it like any other module and then you'll have an admin UI page over at
/admin/reports/upgrade-status which will give you a more in depth overview of all the code on your site including contrib & custom code as well as flagging up any general compatibility issues your environment might have.
Tip: when working with client upgrades I sometimes install Upgrade Status before running any code updates and take a screen print (or export to PDF) to give clients an overview on how compatible their site was with Drupal 9 before the upgrade. I've had some clients wonder why a Drupal 9 upgrade "took so long" as it's be marketed as a much simpler upgrade than before which is of course is but sometimes I've found clients think that means there's no work involved at all & you just hit the "upgrade to Drupal 9" button :). By showing the state of the site beforehand I found it helps clients understand the process & where the hours have gone on the upgrade.
Click the "Check available updates" link at the top to get an initial overview of your code compatibility, after that runs you'll probably have a page with a mixture of green, yellow, grey & blue. As we already updated our contrib modules to Drupal 9 compatible versions in the previous step I'm hoping you are mainly seeing custom code in the grey and yellow areas meaning some action is required.
If you do see some contrib modules in grey or yellow it often is because the Drupal 9 compatible version of the module isn't compatible with Drupal 8 as well so you'll need to wait to upgrade those once your site is running Drupal 9.
I'll assume our contrib code is in good order here so onto our custom code.
Check the checkboxes next to the custom coded components and scroll to the bottom and click the "Scan" button. When the page reloads you should now see how compatible your custom code already is and any problems it might have.
Click the "X problems" link on the project and a modal will show you everything you need to change to bring your custom code up to date with Drupal 9. Here's an example from a site I upgraded:
Run through all your custom code and then run the scan again until you have no problems listed or that you fully understand the problems listed and know they won't be an issue in the upgrade.
If you've been following function deprecation regularly or you just have a custom theme you might find the only code change you'll need is to add the following to you .info.yml files to tell Drupal your code will work with Drupal 9:
core_version_requirement: ^8 || ^9
Depending how much custom code you have & how you like to do things you can install the Drupal Upgrade Rector module and have it update the simpler deprecation issues automatically. I didn't do this on my upgrades as I kind of prefer to review my code at the same time & see if there's anything that could be refactored as part of the upgrades.
One thing with Composer that can actually drive you insane is when you get dependency conflicts when trying to add or update a package. These basically happen due to Composers super thorough way of checking dependencies so that the code you add or update will actually work with your current project setup so while frustrating it's actually doing you a big favour!
For example, if you try to update a module to a specific version which is not compatible with your version of Drupal, Composer will error & revert everything back to how it was before you tried to update the module.
Here's an example from one site I upgraded:
The site was running the Video module (1.4.0) which did not have a module version compatible with both Drupal 8 & 9 , it did however have separate versions compatible with Drupal 8 & 9 (remember I mentioned this in the "Get your Drupal 8 in order!" section above). When I tried to run my Drupal 9 code update using
composer require drupal/core:^9.3.2 --update-with-all-dependencies I got the following:
Problem 1 - Conclusion: don't install drupal/core 9.3.2 - Conclusion: don't install drupal/core 9.4.x-dev - drupal/video 1.4.0 requires drupal/core ^8 -> satisfiable by drupal/core[8.0.x-dev, 8.1.x-dev, 8.2.x-dev, 8.3.x-dev, 8.4.x-dev, 8.5.x-dev, 8.7.x-dev, 8.8.x-dev, 8.9.x-dev]. - drupal/video 1.4.0 requires drupal/core ^8 -> satisfiable by drupal/core[8.0.x-dev, 8.1.x-dev, 8.2.x-dev, 8.3.x-dev, 8.4.x-dev, 8.5.x-dev, 8.7.x-dev, 8.8.x-dev, 8.9.x-dev]. - drupal/video 1.4.0 requires drupal/core ^8 -> satisfiable by drupal/core[8.0.x-dev, 8.1.x-dev, 8.2.x-dev, 8.3.x-dev, 8.4.x-dev, 8.5.x-dev, 8.7.x-dev, 8.8.x-dev, 8.9.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.8.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.9.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.0.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.1.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.2.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.3.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.4.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.5.x-dev]. - Can only install one of: drupal/core[9.3.x-dev, 8.7.x-dev]. - Installation request for drupal/core ^9.3.2 -> satisfiable by drupal/core[9.3.2, 9.3.x-dev, 9.4.x-dev]. - Installation request for drupal/video (locked at 1.4.0, required as ^1.4.0) -> satisfiable by drupal/video[1.4.0].
What Composer was telling me is that version 1.4.0 of the Video module is only compatible with Drupal Core 8.0.x-dev, 8.1.x-dev, 8.2.x-dev, 8.3.x-dev, 8.4.x-dev, 8.5.x-dev, 8.7.x-dev, 8.8.x-dev, 8.9.x-dev and not the Drupal 9.3.2 version I was trying to install. Saved me a broken site so good job Composer but how did I fix this? Actually I found the easiest was just to remove the Video module temporarily from Composer (
composer remove drupal/video) then run my Drupal Core update again. Once Drupal core was updated I could run
composer require drupal/video and Composer would install the right version of the Video module compatible with Drupal 9!
You might find you have a few module issues like this so you can remove all the modules in one go, do the Drupal core update and then add the modules to Composer once again in one go.
I also found managing Drush with Composer to sometimes cause similar issues so I would recommend removing Drush from the project before upgrading core then adding it back again afters.
Finally time for Drupal 9
After all that compatibility work you should now be ready to "flip that D9 switch" and move your codebase up to Drupal 9!!
Again you can do this manually but as I said Composer will make your life easier. I gave a sneak preview of the core update command above but here it is again:
composer require drupal/core:^9.3.5 --update-with-all-dependencies
And that should update your codebase to at least Drupal 9.3.5. If you get any conflicts run through the same steps as above in the "Composer conflicts" section.
Database updates & config export
Last step is to update your database with any Drupal 9 database changes by again running update.php or
drush updatedb. Check the site runs as expected and also review the "Status Report" & database logs for any errors or issues, if everything looks good and you use config management you can then run a new config export to make sure your config will work with Drupal 9. Not sure the last step is strictly needed but I did it anyway.
So there you go, a quick run through upgrading from Drupal 8 to 9 and now Drupal 8 is no longer maintain you should get your Drupal 8 sites upgraded as soon as possible. Thanks for reading!
If you liked this please sign up to my newsletter to get updates on all future posts including news about my YouTube channel which is coming in 2022.