December 22, 2022

Upgrading to Drupal 10 and PHP 8

Part 2 post to get your server & codebase upgraded for Drupal 10

Drupal 10 was officially released last week (December 14th 2022) so to follow up on the preparing Drupal sites for PHP 8 post (a requirement for Drupal 10) in this post I'll run through how I upgraded this site to Drupal 10 and how I upgraded to PHP 8 on a Linode server.

Introducing Drupal 10!!!

After 7 alphas, 2 betas and 2 release candidates (RC), Drupal 10 was officially release on December 14th 2022, another amazing job from the Drupal core team & community.

When to upgrade to Drupal 10?

For a lot of my client work we decided to wait until 2023 to upgrade but we're preparing by getting the Drupal 9.x codebases PHP 8 compatible and upgrading servers to PHP 8. For this site however which is my playground to test & learn new things I decided I wanted to get onto Drupal 10 as soon as an official stable release was ready, and that's exactly what I've done.

Each project is different so determining when to upgrade to Drupal 10 is completely down to project complexity, needs & risk. Do you need something in Drupal 10, do you want to be "cutting edge" and are you prepared for any potential risks/work of being an early adopter?

I want to explain what I mean by risks so it's not seen as a negative.

First one for client work is the logistics of timing, Drupal 10 came out in mid-December as I mentioned so unless you've been tracking development since the early alpha releases the chances are you aren't truly "ready" to upgrade before xmas & the turn of the year when many offices are closed & workers away.
In addition, Drupal 10 now requires PHP 8 which is another overhead to the upgrade despite Drupal warning about this for many months on database updates & the status page.

Drupal status page PHP 7.x warning
Drupal status page PHP 7.x warning

The other factor at least from my experience is that while Drupal core might be stable you will probably find some contrib modules are still buggy or as you'll see below not fully Drupal 10 ready yet (more on that below). And this is one of the things you need to account for if you are an "early adopter": do you have time & resource to upgrade contrib modules or find alternatives/workarounds. I think this all depends on the size & complexity of a project and there is no one size fits all answer.

My stand out changes in Drupal 10?

There's a nice overview from on Drupal 10 readiness which I'd recommend reading but here are some of the things that stand out to me.

  • As mentioned Drupal 10 now requires PHP 8 which given PHP 7.x ended support at the end of November this is a given
  • Support for Internet Explorer is dropped, excellent moving swiftly on........
  • If you maintain your sites with Composer (which you should) you now need at least version Composer 2.3.6
  • CKEditor 5 is now the standard although most my projects use Gutenberg editor instead
  • Olivero is the new default frontend theme & Claro is the new default admin theme.
  • In addition, the Bartik, Classy, Stable & Seven themes have been removed from core and are now available as contrib themes.
  • Base themes are fading away in favour of the new starterkit theme which provides a starter script to build clean starter themes to build on top of. I haven't looked at this much but I'll be testing this soon and will write a blog post on it in 2023.
  • jQuery has been updated from 3.6.0 to 3.6.2 and will slowly be phased out in favour of vanilla Javascript from my understanding
  • Some core modules have moved to contrib modules. These include Aggregator, QuickEdit, HAL, Activity Tracker and the RDF module. This will the goal of making Drupal leaner out the box.

There's plenty more changes in Drupal 10 but these were the ones that stood out for me. Moving on...........

Part 1) Upgrading to PHP 8 on Linode

Along side upgrading my codebase I also needed to upgrade my Linode running PHP 7.4 with Ubuntu 18.04 LTS to PHP 8 and Ubuntu 22.04 LTS.

If you already have your server & local environments running on PHP 8 then you can skip to "Part 2) Upgrading your Drupal codebase" below.

A new Linode

I say upgrade but what I actually did was to build a completely new Linode & transfer my codebase & database from the old Linode to the new PHP 8 Linode. Given I need to upgrade the Operating System I decide it would be easier this way, especially given it was just 2 sites (this site & my DIY blog, - a Wordpress site) I would need to transfer.

One thing I love about Linode minus the freedom & cheap pricing is they have a great documentation library. A quick search brought this nice guide on installing a LAMP on Ubuntu 22.04 LTS which is what I followed to create & setup my new Linode. I won't repeat what is written in the guide so you can follow that directly if you like.

Linode LAMP installation guide
Linode LAMP installation guide

Once you've followed the guide you should have a working Apache server with PHP 8.1, Ubuntu 22.04 LTS, MySQL & a database installed.

Transferring data with rsync

The first thing I synced over from the old Linode was the Apache vHost files, I know this was a bit lazy but well I'm a developer, we like to be efficient :)

From the old Linode I ran: rsync -a --progress VHOST-FILE.conf root@NEW-LINODE-IP-ADDRESS:/etc/apache2/sites-available and then enabled the site in Apache using: sudo a2ensite SITENAME.conf

Now I needed the codebase so I could run drush commands inorder to set the site up on the new Linode.

And the following to catch any . (dot) files & directories (probably a better command for this but it got the job done): rsync -avzP /PATH-TO-OLD-SITE-ROOT/[^.]* SSH-USER@NEW-LINODE-IP-ADDRESS:/PATH-TO-NEW-SITE-ROOT/

TIP: I'd setup a SSH key on the new Linode so no password is needed and the transfer started immediately.

Next I did a database dump with drush on the old Linode using: vendor/drush/drush/drush sql:dump --result-file=tomswebstuff.DATE.sql

Then I rsynced the database dump from the old Linode to the new Linode: rsync -a --progress tomswebstuff.DATE.sql SSH-USER@NEW-LINODE-IP-ADDRESS:/PATH-TO-NEW-SITE-ROOT/

Now I had everything I needed, I'd kept the MySQL credentials the same on the new Linode so I ran vendor/drush/drush/drush status to check everything looked ok which it did.

Time to dump the database on the new Linode: vendor/drush/drush/drush sqlq --file=/PATH-TO-NEW-SITE-ROOT/ and again ran drush status to see all was well.

Change the DNS IP address

I already had a A/AAAA Record for a subdomain on the site so using Linodes Domains UI I updated the IP address for the subdomain to point to the new Linodes IP address so I could test the site first. The TTL was set to 5mins so the change happened quickly. From there I loaded up the subdomain and checked the site over for any issues.

The subdomain looked good so I removed it from the DSN and updated the other A/AAAA Records to to point to the new Linodes IP address, waited for the TTL to resolve and then checked the live domain for any issues.

Everything looked good so I just needed to setup https and I was done.


Using Cerbot makes it quick & easy to add https to your domains. I took some of the information from this Cerbot Linode guide and ran:

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --apache

From there you select the domains you want to add https to and as long as your A/AAAA Records have resolved to the new IP address it should work and even setup a http to https redirect for you.

Now my site was transferred to the new Linode running PHP 8 and it only took about 30mins! At this point my site was still running on Drupal 9.5.0 so it was time to upgrade to Drupal 10 on a local development site first and then pull the codebase onto the server.

Part 2) Upgrading your Drupal codebase

It wasn't long ago I was writing about upgrading from Drupal 8 to Drupal 9 and a lot of what I'll write now about upgrading to Drupal 10 follows the same principles with a few deviations.

Important! Some of the changes listed below can only be done once your codebase is running on Drupal 10 so I created a .txt file "Todo after Drupal 10 upgrade" to note everything I needed to do after the core update is complete.
I'll mark these things with: Add this step to your "Todo after Drupal 10 upgrade" list.

Also important! As I mentioned in the Drupal 8 to 9 upgrade guide I'd recommend doing a full configurations export prior to upgrading anything. As you'll see you may have to temporarily remove some contrib modules and by doing so you'll delete your configurations for those modules, by exporting the config you can then re-import them again after you have upgraded to Drupal 10 and added the modules back.

Upgrade Status

So like with the Drupal 8 to 9 upgrade the first & probably most important step is to install the Upgrade Status module on your Drupal 9 installation and then head over to /admin/reports/upgrade-status and review the state of your Drupal 10 readiness.

Custom Code

This site has no custom code other than a custom theme so I first scanned that theme & fixed any issues flagged by the Upgrade Status report. For me as I'd kept the theme up to date with 9.5.0 there only 3 simple issues to fix:

Drupal Upgrade Status theme fixes
Drupal Upgrade Status theme fixes

Call to deprecated function drupal_get_path(). Deprecated in drupal:9.3.0 and is removed from drupal:10.0.0. Use Drupal\Core\Extension\ExtensionPathResolver::getPath() instead.
This was fixed by converting drupal_get_path() calls to Drupal\Core\Extension\ExtensionPathResolver::getPath() instead.
If you make this change now your Drupal 9 installation will break so you'll need to wait until your codebase is running on Drupal 10. Add this step to your "Todo after Drupal 10 upgrade" list.

The second was actually an error so I just removed it and the last issue I simply changed core_version_requirement: ^9 to core_version_requirement: ^9 || ^10 in the themes .info.yml file.

Something that Upgrade Status didn't & couldn't flag up was that my theme was using the "Stable" theme as a base theme (base theme: stable in my .info.yml file. As mentioned above, Stable is no longer part of Drupal core for Drupal 10 but can be downloaded as a contrib theme instead. I didn't want to do this now as I was still running Drupal 9.5.0 but I made a mental note for later on to add the Stable contrib theme. My plan is to rebuild the theme using the new Starterkit theme instead, more on that in a future post.
Add this step to your "Todo after Drupal 10 upgrade" list.

Contrib Modules

Drupal contrib modules not ready for Drupal 10
Drupal contrib modules not ready for Drupal 10

Next up I needed to see if all my contrib modules were Drupal 10 ready and sadly they weren't, 5 modules were marked as "Incompatible", after reviewing I realised 1 of them was not in use so I used Composer to remove it completely (composer remove drupal/PROJECT-NAME).

That left me with 4 modules not ready for Drupal 10, damm!!

I headed over to the issue queues for each module & checked the automated Drupal 10 issues created by the bots. As it turned out only Formdazzle did not have an appliable patch or branch for Drupal 10 readiness.

Example of "Automated Drupal 10 compatibility fixes" issues
Example of "Automated Drupal 10 compatibility fixes" issues

For Rabbit Hole which had a beta release I changed my composer.json "minimum-stability": "stable" line to be "minimum-stability": "dev" and then I was able to get a beta version (composer require 'drupal/rabbit_hole:^1.0@beta') of the module which was Drupal 10 ready, yaay!

As for the other modules, given time constraints I decided to remove Formdazzle completely as it was only used on 1 form and for Lazy & Social Media I decide to download them directly from their git repositories and apply the automated patches using cweagans/composer-patches.
I had to download those modules this way because Composer would not let me download them from since they don't have a release marked as compatible with Drupal 10. I'll do a follow up post on this but in the meantime if you need to do similar checkout this post on Composer: How to use Git repositories.
Add this step to your "Todo after Drupal 10 upgrade" list.

Uninstall deprecated modules & themes

Next up, remove any depreciated modules from your installation. A reminder of which modules these were: Aggregator, QuickEdit, HAL, Activity Tracker and the RDF module and the Bartik, Classy, Stable & Seven themes.

If you go to /admin/modules/uninstall you'll see a "Deprecated" link next to any deprecated modules which you have installed.

Deprecated module link
Deprecated module link

If you still need any of these you can download (composer require drupal/PROJECT-NAME) them from contrib after you have updated Drupal core to Drupal 10. Optional: Add this step to your "Todo after Drupal 10 upgrade" list.

For themes you just need to access the Appearance page and it'll remove the themes.

Also you'll want uninstall Upgrade Status & remove it from Composer, it's designed for the previous version of core than the one you are upgrading to so it work with on Drupal 10 until we have Drupal 11 :)

IMPORTANT! Temporarily remove Drush

I always install Drush as a vendor package on projects but when doing upgrades it can prevent core for upgrading I've found. You might hit Composer conflicts like this error for example:

Your requirements could not be resolved to an installable set of packages.
Problem 1
- Root composer.json requires drupal/core-recommended 10.0.0 -> satisfiable by drupal/core-recommended[10.0.0].
- drupal/core-recommended 10.0.0 requires psr/container ~2.0.2 -> found psr/container[dev-master, 2.0.2, 2.0.x-dev] but these were not loaded, likely because it conflicts with another require.

To avoid conflicts between Drupal core & the Drush version you are running I also remove Drush from Composer temporarily prior to updating Drupal core.

  • Remove it: composer remove drush/drush
  • Run your core Composer updates (see the next step).
  • Then add Drush again: composer require drush/drush (Composer will find the right version of Drush you need for your install).

Upgrading to Drupal 10 with Composer

If you've got this far you've done well and we're nearly there, now it's just to run the Composer update commands for Drupal core and we should be done!

composer require drupal/core-recommended:10.0.0 drupal/core-composer-scaffold:10.0.0 drupal/core-project-message:10.0.0 --with-all-dependencies
Successfully upgrading to Drupal 10 with Composer
Successfully upgrading to Drupal 10 with Composer

Hopefully you'll see a lot of packages downloading the most important ones being:

  - Upgrading drupal/core-composer-scaffold (9.5.0 => 10.0.0): Extracting archive
  - Upgrading drupal/core-project-message (9.5.0 => 10.0.0): Extracting archive
  - Upgrading drupal/core (9.5.0 => 10.0.0): Extracting archive
  - Upgrading drupal/core-recommended (9.5.0 => 10.0.0)

Oh remember that "Todo after Drupal 10 upgrade" list

You now have the Drupal 10 codebase installed and there will be some database updates to run but to avoid a tonne of warnings & potential errors we need to run through that "Todo after Drupal 10 upgrade" list:

  • Fix any custom code fixes related to deprecated functions like the Call to deprecated function drupal_get_path() error I got with my theme
  • Install any deprecated core Drupal 9.x modules & themes now part of contrib, for me I was using Stable as a base theme so I added the Stable contrib theme (composer require drupal/stable)
  • Add repository information and patches to composer.json for any modules needing patches to make them Drupal 10 ready.

Now you can run the database updates using drush updatedb, then drush cr, then check out your site and go crack open that beer as you're (hopefully) now running Drupal 10!!

Drupal 10 status page
Drupal 10 status page

My site is now upgraded to Drupal 10 on my local DDEV environment and will be deployment to production in the near future. This site is now running on Drupal 10!