Copying A Drupal Site To A New Host

From time to time, I have had to “copy and paste” a Drupal site to a new server. Sometimes it is for upgrading the underlying OS, which prevents simply copying the VM wholesale (if’re you’re using Docker, good for you but we aren’t all in a position to do that). This involves copying the database, content files and configuration files to a new LAMP host which usually results in some level of unanticipated difficulty and troubleshooting so I am writing this post to serve as a guide and checklist to head off any common problems or difficulty.

Step One - Preparing the New Host

Let’s break this down into the LAMP acronym, shall we?

Linux

Install your favorite distro. I like Ubuntu and CentOS and will assume you will use one of those, but feel free to use whatever you like. Procuring a VM/VPS and installing an OS to it is outside the scope of this document.

Apache

Apache is extremely mature and feature complete, so installing whatever version is available in your distro’s main repository through apt or yum should be fine. If you’re using a Debian-based system, you’ll likely need to enable common modules, such as mod_ssl and mod_rewrite using a2enmod.

MySQL

MySQL has new features often enough and the distro repo is probably using an old enough version that you don’t want to use it. Luckily MySQL provides repositories with newer versions and provide security patches quickly. Here is the guide for yum and here is the guide for apt.

Be sure to run mysql_secure_installation, as well as create a database for Drupal to use and create a user with only access to the Drupal database.

CREATE DATABASE drupal; -- Create database
CREATE USER 'drupal'@'localhost' IDENTIFIED BY 'password'; -- Be sure to use a secure password!
GRANT ALL PRIVILEGES ON drupal.* TO 'drupal'@'localhost'; -- Change database name and user name as needed
FLUSH PRIVILEGES; -- Don't forget this!

PHP

PHP does not provide a package manager repo and your distribution of choice may be using an unacceptably old version. To check the currently available version from the default repos, run yum search --showduplicates php or apt -s install php. As of this writing, CentOS 7 provides PHP 5.4 (released 9 years ago, EOL 6 years ago!) while Ubuntu 20.04 provides PHP 7.4 (released 2 years ago, still in active support). If you don’t want to use your distro’s repo, Remi provides one for yum here and Ondrej provides one for apt here. Once the appropriate repo is installed and configured, install PHP with your package manager. For yum-based systems, using sudo yum install php php-common php-opcache php-mcrypt php-cli php-gd php-curl php-mysqlnd php-xml should get the common packages that should suffice for most needs. For apt-based systems, use sudo apt install php libapache2-mod-php.

Step Two - Copying Data

Database

On the old box, log in to the Drupal web GUI and put the site in maintenance mode. This isn’t inherently necessary, but helps ensure you don’t copy the database in an awkward state. Now log in via ssh and run mysqldump -u mysqlusername -p --databases databasename > drupal.mysql. Note you do not need elevated permissions for this, any MySQL user with sufficient read privileges on the database will suffice. Take the site out of maintenance mode and copy the database over to the new server. On the new box, run mysql -u root -p < drupal.sql. Some notes here: different versions of MySQL create slightly different versions of dumped output, especially if you throw MariaDB in the mix. If you get any error messages, open your SQL dump in your favorite text editor. You can modify lines as needed (e.g. if MySQL is trying to create an already existent database). I am using the root user because you will likely run into permissions issues setting some of the SQL modes if you use an unprivileged user.

Files

Copying files is straightforward enough, but the trick here is ensuring that you get them all, including the non-web files. The web files are probably somewhere in /var/www, copy those. On Debian-based systems, the Apache config files are in /etc/apache2/sites-available. Grab the relevant ones and copy them over (don’t forget to run a2ensite on the new box). On RHEL-based systems, the Apache config files are stored in /etc/httpd/conf as well as /etc/httpd/conf.d. If you’re upgrading from an Apache 2.2 system to an Apache 2.4 system, you will likely need to make extensive modification to the Apache config files. See this Apache article. If your old SSL cert covers the new host (using a wildcard cert, SAN cert, or not changing the hostname), grab your SSL cert and private key. The location of those files will be listed in your Apache config. Anything special in my.cnf, php.ini, cron or /etc/hosts? Copy those as well. Pay special attention to if you need to copy settings from MySQL or PHP but will be copying to a server with upgraded versions of these applications - syntax of the config files may have changed.

Update for new host

There are likely a number of places where the old hostname, database name, database user and database password are listed and must be updated. Let’s start with Apache config: Any place the old hostname exists, replace it with the new. We could write a script to do this automatically, but I’m a believer in knowing what changes you’re making and opening the files and looking at them to understand the context. The old hostname is likely listed as the ServerName or ServerAlias in one or more .conf files. Update these to the new hostname. You’re likely running a rewrite line that rewrites HTTP to HTTPS - the old hostname is likely hardcoded here, too. Here we’ll use grep to test - grep -r "oldhostname.example.com" /etc/httpd OR grep -r "oldhostname.example.com" /etc/apache2 should yield no results. Fix any stragglers.

Now let’s move on to the webroot. The main spots to check here are $DRUPAL_HOME/.htaccess, $DRUPAL_HOME/sites/default/settings.php, and $DRUPAL_HOME/sites/default/services.yml. Note that settings.php likely contains your database settings, so pay extra attention there. Once again, once you’re done your first pass, run grep -r "oldhostname.example.com" /var/www to catch anything you may have missed. You should also grep for any database values changed (user, password, database name, etc).

If you copied my.cnf or php.ini over, they likely do NOT include specific references to the hostname and should be good to go as is. Doesn’t hurt to check and confirm though.

Did you have anything special set in /etc/hosts? Update that as well.

Clear Drupal cache

Drupal caches many of its file system configurations in its database, which means that all the changes we just made likely won’t take effect until we clear the cache. Log in to MySQL (mysql -u drupal -p) and USE your Drupal database. Truncate (DELETE FROM table_name;) all table names beginning with cache. I’ve specifically had trouble with cache_container caching values of services.yml.

Step Three - Test, Identify Issues, Remediate, Repeat

Your DNS record is set, Apache and MySQL are running under systemd - time to test! Navigate to your new hostname and see how it goes. If you copied the database from maintenance mode, your site should be in maintenance mode now. Log in and before you disable that, navigate to https://newhost.example.com/update.php and go through the update.php process. Once complete, disable maintenance mode and commence testing. This is especially important if you upgraded applications between hosts.

If something isn’t working, never fear. Start with the Apache logs, /var/log/httpd/error_log or /var/log/apache2/error.log. You may need to install or enable an additional PHP module. If there are no errors there, check the Drupal logs. If your new install is working well enough that you can successfully log in and navigate the Drupal admin pages, you can view the logs there. If your install is in a poor state, we can still access the logs. We can log in to MySQL and read them directly from the database. Log in (mysql -u drupal -p), select your database (USE drupal), and run SELECT * FROM watchdog ORDER BY timestamp DESC LIMIT 10;. This will grab the 10 most recent entries, which should hopefully give you some insight into the issue. These entries are made to be rendered by PHP and contain PHP arrays which can be difficult to read from the database directly, but shouldn’t present an insurmountable challenge. I do find them significantly easier to read in the MySQL CLI which simply displays them as-is than MySQL Workbench which shows them as a “blob” and you need to right click into each one to view the actual text.

Epilogue

That’s it! You have now successfully copied a Drupal site to a new host.

categorized as: drupal