Technology Solutions for Everyday Folks

Moving a Legacy Drupal Stack to a New Server Host

One of my "end of 2021 break" projects was a planned "lift and shift" of my primary Drupal instance to a fresh, sparkly new web host stack. The stack on which it resided was reaching end of life and for a few other reasons it was time to make the change. In preparation, over the last year or so I've been de-coupling and untangling some of the baggage that had accumulated on the old server and its structure over time. Relatively simple things like straightening out, consolidating, or consistently applying vhost configurations. This significantly reduced the number of moving parts in a larger host migration in that by the time I got to doing the big Drupal instance there were only two remaining things to untangle (I will write more about this in an upcoming short series).

Still, the move to an entirely new server stack has a lot of moving parts and potential points of failure. For this reason I held off on the big migration until I had enough dedicated time to the job. In addition to migrating to a new server stack, I was going to change my Drupal configuration away from the "Legacy" (e.g. originally installed pre-Drupal 8.8.0 codebase) and to the better Composer-manged structure for updates and such.

T-1.5 Weeks Before Migration

"It's always DNS" -- The Internet

A week or so before the targeted cutover, I switched a load of DNS TTLs to a much shorter duration. Most were switched to a 1 hour TTL, but in switching TTLs I noted a few that I would (later) change to 10 minutes for the actual cutover, just to maintain continuity and reduce the "limbo" time as the big change took place.

At this time I also settled on the Virtual Private Server (VPS) configuration I would be moving to, along with selection of OS. I decided to go with Ubuntu for the OS over something like CentOS, mostly due to my existing "muscle memory" for Ubuntu via the Windows Subsystem for Linux (WSL) I already use daily. At work I interact with a lot more CentOS than Ubuntu so I wasn't afraid of using that as an OS; ultimately I chose Ubuntu so I could maintain a consistent set of aliases for things like updates. Muscle memory wins!

T-4 Days Before Migration

I bought into the new VPS configuration/plan and let that dust settle. A fresh VM clone of Ubuntu was obtained, and I created a DNS entries for the new server/host itself.

T-3 Days Before Migration

I spent this day building out my new shiny server stack. Without going into great detail, other than noting it's much easier with today's OS's to build out a clean server with the command line utilities, my basic recipe/process was as follows (for a LAMP stack):

  1. Set up Key Authentication. I've written about this before, but this time I used the better Ed25519 algorithm for my keys.
  2. Install available updates on the base config (I use an alias for sudo apt update && sudo apt upgrade).
  3. Install the basic applications/dependencies, primarily via apt:
    1. Apache, with a basic vhost definition for the server/host after disabling the default vhost.
    2. Certbot, followed by a "trial" run with the server/host vhost to make sure everything's good to go.
    3. MySQL, followed by its "hardening" script/process (mysql_secure_installation) to get things in order.
    4. PHP.
    5. Additional PHP modules, namely things like php-dom, mbstring, gd, and curl; YMMV.
    6. Composer.
    7. Configuring vhost configuration(s) to support Clean URLs for Drupal, which I actually implemented before installing or configuring Drupal. For my setup, this was super important to have working before cutting over the site due in large part to the last bits to untangle from old configurations.
  4. Install Out of Box Drupal and verify the host configuration is happy on a throwaway/test vhost and ready to support the core.
  5. Configure server firewall and other basic security measures.

T-2 Days Before Migration

With a fresh OOB installation of Drupal, I did a "test port" of the file structure and database to a test vhost. Drupal has great documentation to make a development site, and this information is exactly how one does the actual port of an instance to production. This test/process ensured my new host was set up and ready to go for the cutover...at least mechanically so. The general gist of the process is:

  1. Copy/Port the database to the new host;
  2. Copy/Port the file structure to the new host; and
  3. Edit settings.php in a few places as it relates to the new host configuration.

T-1 Day Before Migration

This was intended to be my "catch up" day for anything weird, but since things had gone well thus far I only had a few minor things to do, including changing key DNS records to have a temporary (through migration) 10 minute TTL.

It was also the day to set up the "legacy" host for the remaining bits to be untangled from the Drupal instance. The easiest way to explain this is that I had a few directories of content that needed to persist outside of the Drupal instance, but at the same web host path as the instance. So I had a "dirty" web directory. These dependencies were all external references using these legacy paths.

What I did in this case was create a separate vhost with a subdomain. The legacy paths were copied to this vhost so they can be accessed "cleanly" and afford a full removal from within the Drupal instance (after migration). I could handle traffic with simple Redirect directives in the new vhost configuration, which would allow the legacy material to be seamlessly referenced in this New World Order.

I also brushed up on the process for adding Composer to a Drupal instance, since I would be making that change during the production migration. While I was at it, I installed Drush Launcher as a convenience step for Future Me.

Lastly, I set up the new production vhost configuration so it would be ready to go and so I'd have a place to copy the site. This was important especially for the certbot/SSL configuration. I added all the ServerAlias directives in the configuration, but commented them all out. This was pre-staging for real migration, when I would need to change DNS records and shortly/"immediately" afterward run certbot to get new certificates installed with as minimal impact/downtime as possible.

Day of Migration

Honestly, the day of migration activities were pretty mechanical. I'd moved enough of the key bits in my testing over the previous few days that the "production" move was pretty straightforward. Merging the concepts and workflow for Adding Composer to Drupal and Making a Development Site, my actual process looked like this:

  1. Install a fresh/OOB installation of Drupal (with Composer).
  2. Update one of the DNS records (I have many pointing to the same instance) to the new server so it's ready to go.
  3. Live copy the database between hosts. I personally used the database copy between hosts functionality in HeidiSQL to do this, but there are several ways to accomplish the same task with a tarball or drush commands.
  4. Copy the files, modules, libraries, and themes to the new OOB/Composer Drupal instance (steps 2 and 3 in the Adding Composer documentation). Additionally, this was the step in which I updated settings.php for the new host and database.
  5. Manually add the module definitions to the composer.json configuration file. This was honestly the worst part of the entire process, because I ran into a few modules that were unhappy and that needed a manual "refresh" (reinstall) even though they were up to date. I used both steps 4.2 and 4.3, but used 4.3 first and 4.2 for the half-dozen modules that were just unhappy.
  6. Run the database updater (drush updb) and clear out the Drupal cahce (drush cr) and give it a test!

For a bit of clarity, in step 2 above I changed the DNS A record for one of the alias domains to the new host. I also used certbot to install a fresh certificate for the alias domain so I could enforce HTTPS out of the gate. This vanity/alias domain was my "canary" test following step 6.

Fortunately, giving the site a test hit on the temp/alias domain worked beautifully, and I was able to log into the newly ported (and converted) Drupal instance without issue. Better yet, the Status Report administrative page verified everything was behaving and happy. Actually happier than it had been on the old host due to some permission-like changes I'd made in all this porting!

The second-to-last step of the migration was to change all the DNS records to point at the new host by changing their A records. At the same time, I switched them all back to 1 hour TTLs while the dust settled.

The last step was to uncomment all remaining ServerAlias directives from the vhost configuration, reload Apache, and run certbot to pull and install a fresh set of certificates.

Finally, A Break/Sanity Check

Doing some basic sanity checking and host/browser/cert testing, I verified everything was good and happy. Fortunately, I ran into no problems during the migration and everything was behaving stable and as expected!

T+1 Hours After Migration

Things were still good, so it was time to remove the site and its configuration from the old host. Most of that involved dropping the database and a very pleasing rm -rf * after I'd pulled tarballs of both to an archive "just in case."

It was at this time I also "installed" the legacy path Redirects on the new vhost to keep the few things still pointing at those resources working. The basic gist is as follows:

# "Legacy" paths preserved and shifted to legacy.host.com
    Redirect /pathone/ https://legacy.host.com/pathone/
    Redirect /pathtwo/ https://legacy.host.com/pathtwo/
    Redirect /paththree/ https://legacy.host.com/paththree/
    Redirect /pathfour/ https://legacy.host.com/pathfour/

A quick URL verification later ("yup, those point at the now-legacy host"), and I had a fully-ported and CLEAN site for Drupal...while maintaining legacy pointers to older material which, at the time of migration, hadn't been moved or deprecated.

T+1 Day After Migration

I kept a loose eye on server performance and looking for anything particularly out of order. Fortunately, everything still looked good, so it was time to finally relax and bask in the success. In the longer term, it'll be far easier to maintain the Drupal instance now that it's clean and managed with Composer instead of the old way I was doing things.

T+1 Week After Migration

The "last" step in all of this seeing no weird activity in monitoring was to make the "final" switch and re-up those DNS TTLs to 1 Day or longer from their transitory 1 Hour setting. Just a lot of minutiae, really, but also a good opportunity re-verify that other DNS settings are still consistent across the hosts. Items like CAA records, SPF/DKIM/DMARC records, and also an opportunity to do some housecleaning.

A Good Place to Be From!

Some of this business has been nagging at me for a while, and I had been plugging away and untangling the better part of 25 years' worth of bloat. Fortunately, when it came time to do the "last mile" of conversion (that which I wrote about in this post) the remaining bits went really smoothly. I feel significantly better about the state of the host configuration at this point, and I have presumably saved Future Me from several headaches as things change in the future.

It took a little bit of planning, and the obvious "regular bits of life" injected throughout the process. But it can be done, and thanks to more modern tools and libraries available, can be done in a far more efficient and expedient manner than once upon a time!