Fast and Easy WordPress Migration with WP CLI.
Migrating a WordPress website from one server to another can be a daunting task, especially if you’re not familiar with the process. However, with the help of WP CLI, migrating WordPress can be a breeze. In this article, we’ll walk you through the steps required to migrate your WordPress website to a new server using this powerful tool. By the end of this guide, you’ll be able to transfer your WordPress site to a new server with ease, ensuring that your website remains online and accessible to your visitors. So let’s get started!.
Make sure you have WP CLI installed on both the source (old) and destination (new) servers. If WP CLI is not yet installed you can follow this tutorial.
In this tutorial, we assume you are working with Ubuntu and you are logged in as root, so switch to root user if you are not already.
$ sudo -i
For convenience and to avoid errors, we put the domain name of our new WordPress site in a variable DOMAIN.
export DOMAIN="example.com"
Check that the variable contains the correct information.
echo $DOMAIN
Which should give the domain name you just specified.
example.com
Navigate to the document root of your domain on the old server.
If you followed our previous tutorial, by default you will find the document root of our virtual hosts in “/var/www/virtual/” and the public directory is “htdocs” (i.e., /var/www/virtual/example.com/htdocs). Of course, if this is different on your server you need to change the location accordingly to go to the document root of the WordPress site.
cd /var/www/virtual/$DOMAIN/htdocs/
Export the WordPress database with WP CLI.
wp db export ./wp-db-$DOMAIN.sql --allow-root
Success: Exported to './wp-db-example.com.sql'.
Make temporary directory.
export TMP_DIR=$(mktemp -d) && echo $TMP_DIR
Backup all files in the document root (incl. the database dump file) and put it in a tar file called “example.com.tgz which we store in the temporary directory we just created.
tar -vczf /$TMP_DIR/$DOMAIN.tgz ./
If you have direct access to the new server via SSH, you can transfer the backup file directly to the new server. In this case, we log in with the “root” user and copy the backup file to the “/tmp” directory of the new server. Of course, you need to change port “<ssh-port-new-server>” and IP address “<ip-new-server>” to those of the new server, but that goes without saying.
scp -P <ssh-port-new-server> /$TMP_DIR/$DOMAIN.tgz root@<ip-new-server>:/tmp
Of course you can replace the user “root” with any other user which has permission to write to the “/tmp” or if not chance this directory to any other directory with write permissions for this user.
If you do not have direct access to the new server via SSH (for example, if you are working with containers), you may choose to first transfer the backup file to a separate backup server or a server that can temporarily serve this purpose.
scp -P <ssh-port-backup-server> /$TMP_DIR/$DOMAIN.tgz user@<ip-backup-server>:/backup/path
Also change port “<ssh-port-backup-server>” and IP address “<ip-backup-server>” to those of the backup server, but that goes without saying.
Of course, you must first download the backup file from the backup server onto the new server, but we’ll see that later.
To cleanup remove the database dump file and the temporary directory with tar file.
rm -r /$TMP_DIR/ ./wp-db-$DOMAIN.sql
Switch to the new server and login as root.
We assume that on the new server Nginx has already been configured with a server block for the associated domain and that a document root has also been created as explained in this tutorial.
For convenience and to avoid errors, we put the domain name of our new WordPress site in a variable DOMAIN.
export DOMAIN="example.com"
Check that the variable contains the correct information.
echo $DOMAIN
If you have followed the previous steps, you can find the backup file in the “/tmp” directory. Again, we assume that the same directory structure was followed and that the document root can also be found in /var/www/virtual/example.com/htdocs”. If this is not the case, change this.
Should you have chosen to transfer the backup file to a separate backup server first, we still need to download and save it to the “/tmp” directory. If not, you can skip this step.
scp -P <ssh-port-backup-server> user@<ip-backup-server>:/backup/path/$DOMAIN.tgz /tmp
Having the backup file in the “/tmp” directory, we can extract it and place it directly in the document root.
WARNING: All content present in the document root will be overwritten and thus permanently destroyed!
If you are not sure, you can check again what is currently in the document root and whether this effect can be overwritten.
ls -al /var/www/virtual/$DOMAIN/htdocs/
Which in our case gives the following result:
total 2 drwxr-x--- 2 www-data www-data 3 May 3 15:39 . drwxr-x--- 3 www-data www-data 3 May 3 15:39 .. -rw-r----- 1 www-data www-data 413 May 3 15:39 index.html
So here we are talking about a simple “index.html” file that we are going to delete later.
Now that we know nothing will be overwritten, we can safely extract the files with the following command:
tar -xzvf /tmp/$DOMAIN.tgz -C /var/www/virtual/$DOMAIN/htdocs/
Navigate to the document root of your domain.
cd /var/www/virtual/$DOMAIN/htdocs/
Delete the “index.html” that we don’t need because WordPress has its own index.php file that starts the site.
rm index.html
We read the database name, user and password from the wp-config.php file and put them in the corresponding variables.
export DB_NAME=$(grep DB_NAME wp-config.php | awk -F\' '{print$4}') && \ export DB_USER=$(grep DB_USER wp-config.php | awk -F\' '{print$4}') && \ export DB_PASSWORD=$(grep DB_PASSWORD wp-config.php | awk -F\' '{print$4}')
Create the new database.
mysql -e "CREATE DATABASE $DB_NAME" && mysql -e "FLUSH PRIVILEGES" && \ mysql -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO $DB_USER@localhost \ IDENTIFIED BY '$DB_PASSWORD'" && \ mysql -e "FLUSH PRIVILEGES"
Import the WordPress database dump file using WP CLI.
wp db import ./wp-db-$DOMAIN.sql --allow-root
Success: Imported from './wp-db-example.com.sql'.
Set the correct owner and permissions.
chown -R www-data:www-data ./ && \ find ./ -type d -exec chmod 750 {} \; && \ find ./ -type f -exec chmod 640 {} \;
Delete the database dump file and backup file.
rm /tmp/$DOMAIN.tgz ./wp-db-$DOMAIN.sql
A final point is to check that the url that WordPress has stored matches the url that nginx is configured with. By default our configuration refers to naked domain so without “www” in other words example.com and not www.example.com and non SSL requests on port 80 are forwarded to SSL on port 443. If the url in WordPress is different this can cause a lot of problems and often an endless loop.
We can read the url stored by WordPress with the following command:
wp option get home --allow-root
https://www.example.com
As you can see in this example, WordPress has “https://www.example.com” as the url while nginx will want to redirect that to “https://example.com” causing a loop. Another common case is the absence of “https” in the url that WordPress has stored (e.g. http://example.com) while Nginx redirects every “http” request to “https”.
To solve this issue, we can use the WP CLI command “search-replace”, which finds all references to the wrong url in WordPress and replaces it with the specified new url.
We retrieve the current url used by Worpress again and store it in the variable “OLD_DOMAIN”.
export OLD_DOMAIN=$(wp option get home --allow-root)
And then we run the WP CLI “search-replace”. We do this in two stages, the first stage is with the “–dry-run” parameter to check for errors that could possibly cause a crash.
wp search-replace "$OLD_DOMAIN" "https://$DOMAIN" --skip-columns=guid --allow-root --dry-run
If no errors occur, the command can be run without the “–dry-run” parameter.
wp search-replace "$OLD_DOMAIN" "https://$DOMAIN" --skip-columns=guid --allow-root
If you want to run the site under a completely different domain name, you can use the WP CLI “search-replace” command. In this case we put the new domain name in the variable “NEW_DOMAIN”.
export NEW_DOMAIN="example1.com"
First run the WP CLI with the “–dry-run” parameter to check for errors.
wp search-replace "$OLD_DOMAIN" "https://$NEW_DOMAIN" --skip-columns=guid --allow-root --dry-run
If no errors occur, the command can be run without the “–dry-run” parameter.
wp search-replace "$OLD_DOMAIN" "https://$NEW_DOMAIN" --skip-columns=guid --allow-root