Anyone who has been using WordPress for a while has probably had at least one case where an update or change didn’t work out as planned, and you had to revert to backups.

Or maybe you want to try some development on your site and need the results to be live on the internet.

Whatever the reason, when creating a clone of your website to be hosted at another address (for example test.website.com, next to website.com) you have probably followed the Codex page on moving your site and spent some time changing the configuration and searching and replacing the old URL entries from your database. A lot of hard work and you often end up missing something small.

Today we will look at an alternative that lets you automate this, and many other chores, thanks to the work done by the developers of the WP-CLI project.

WP-CLI is an open source set of command line tools for working with your WordPress installation instead of going through the WordPress admin screens. It also includes many tools to make your life easier, allowing you to search and replace inside the database, make backups, install and update plugins and work with users.

Many plugins also provide hooks for WP-CLI, allowing you to empty your caches for W3-Total Cache from the command line for example.

Why do this from the command line?

It can be more efficient and you can script and schedule tasks as required, which as you’ll see will make it easier to get things done.

Unlike most plugins, WP-CLI is not a plugin to install into WordPress, but rather a set of tools based on PHP that need to be installed on your server. To function properly it needs a WordPress install to work with, but as you will see, installing WordPress through WP-CLI is extremely convenient and fast.

These instructions will assume a few things:

• You have SSH access to your hosting account and are comfortable using it.

• You have setup a subdomain for your website (for example test.website.com next to website.com ). This can be empty.

• You can have a second MySQL database.

• You have backups in case something goes wrong.

To start, you should have made the subdomain, with its own files and an extra database. Every hosting control panel likes to store its files in different places, so you’ll need to figure out where your test site is.

For example, cPanel will create the directory to store your web files in /home/youraccount/public_html/test/

(should you be following the test convention).

Virtualmin and DirectAdmin will create

/home/youraccount/public_html/

and

/home/youraccount/domains/test.website.com

I’m using Virtualmin for my examples, so please adjust your commands as required. Also write down the login data for your databases as you’ll be needing them.

From here on we’ll refer to the test location as $TEST_HTML_DIRECTORY and the main site as $MAIN_HTML_DIRECTORY . These can be defined on your shell using:

# These are my defaults for using Virtualmin, please substitute your own paths $ export MAIN_HTML_DIRECTORY=/home/website/public_html $ export TEST_HTML_DIRECTORY=/home/website/domains/test.website.com/public_html

Technically nothing is preventing you from moving your staging site to another server, but describing that step by step is beyond the scope of this tutorial. Now obviously, if you do manage to get this working, scripting this should prove to be an easy exercise. But it will be one that I’ll leave up to the reader.

Once you have ensured that you have made full backups of your site, we’ll get started…

Installing WP-CLI

To install WP-CLI we’re going to follow the simple instructions left on http://wp-cli.org/

Log in to your account using SSH and execute the following commands in your home folder:

$ curl https://raw.github.com/wp-cli/wp-cli.github.com/master/installer.sh | bash % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 105 1891 105 1891 0 0 4462 0 --:--:-- --:--:-- --:--:-- 14435 Installing Composer in /home/website/.wp-cli ------------------- #!/usr/bin/env php Some settings on your machine may cause stability issues with Composer. If you encounter issues, try to change the following: Your PHP (5.3.3) is quite old, upgrading to PHP 5.3.4 or higher is recommended. Composer works with 5.3.2+ for most people, but there might be edge case issues. Downloading... Composer successfully installed to: /home/website/.wp-cli/composer.phar Use it: php composer.phar Installing WP-CLI in /home/website/.wp-cli ----------------- ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing mustache/mustache (v2.4.1) Cloning 14bc3b4f6a4770b535fc433bfd0435471ed386c6 - Installing wp-cli/php-cli-tools (v0.9.3) Cloning 339f50279cc7494b87343689ef664896bd5b5dc1 - Installing wp-cli/wp-cli (v0.11.2) Cloning 9d00185d49d0cdb16d25dddba69943394830ea95 wp-cli/wp-cli suggests installing d11wtq/boris (Enhanced `wp shell` functionality) Writing lock file Generating autoload files WP-CLI files have been successfully installed. To test WP-CLI, run: /home/website/.wp-cli/bin/wp --info Make sure you have the following line in your .bash_profile file: # WP-CLI directory export PATH=/home/website/.wp-cli/bin:$PATH

After installing WP-CLI, you’ll see that there’s information on how to setup your PATH (in the BASH shell) to easily reference the wp command from anywhere in your WordPress install. Being able to run that code from anywhere is central to the remainder of this tutorial, so let’s go ahead and get that configured.

It’s best to edit the .bash_profile file by hand. In our case it was empty so we can just create it:

$ echo 'export PATH=/home/website/.wp-cli/bin:$PATH' >> ~/.bash_profile $ source ~/.bash_profile

In this case we haven’t installed WordPress yet, so let’s use WP-CLI to turn the 5 minute install into a 5 second install. If you have WordPress installed, you can safely skip to “Fun with WP-CLI” below.

$ wp core download Downloading latest WordPress (en_US)... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 3934k 100 3934k 0 0 709k 0 0:00:05 0:00:05 --:--:-- 1039k Success: WordPress downloaded. $ wp core config --dbname=website --dbuser=website --dbpass=H3S7g7vkzQwGvk --dbhost=localhost Success: Generated wp-config.php file. $ wp core install --url=http://website.com --title="Just another test site" ----admin_name=barry [email protected] --admin_password=letmein123! Success: WordPress installed successfully.

Okay, so technically this took 7 seconds, but at this point your site will be installed. Let’s quickly break down what just happened above.

1. wp download retrieves the latest version of WordPress. You can specify a version and language if you want.

2. wp core config generates a clean wp-config.php file using the settings in the parameters. No edits required.

3. wp core install installs a simple WordPress site with the chosen admin email and password. Be smart and include the admin_name parameter or else you’ll end up with “Admin.” You should also receive an email after this is done, just as with a regular install.

Fun with WP-CLI

Now that we have WordPress installed, let’s test that WP-CLI works:

$ wp cli info PHP binary: /usr/bin/php PHP version: 5.3.3 php.ini used: /etc/php.ini WP-CLI root: /home/website/.wp-cli/vendor/wp-cli/wp-cli WP-CLI config: WP-CLI version: 0.11.2

And let’s check and make sure that WP-CLI will work with WordPress. You need to be in the main directory ( $MAIN_HTML_DIRECTORY )

$ wp core version --extra WordPress version: 3.6 Database revision: 24448 TinyMCE version: 3.58 (358-24485)

We should at this point make a backup of the database for use in the next step and general safekeeping. This can be done with the wp db export command:

$ wp db export ~/db-export.sql Success: Exported to /home/website/db-export.sql

Be sure never to store your MySQL backup in any folder below public_html (or your server’s docroot) as technically it could be downloaded that way.

Extra precautions

To help prevent mistakes we can limit what WP-CLI can do to your website by creating a wp-cli configuration file, wp-cli.yml .

For example, we can specify the following in wp-cli.yml :

disabled_commands: - db drop - search-replace

Please note that you will need to insert tabs in front of the disabled commands.

Now let’s try running one of those disabled commands:

$ wp search-replace testingasdf testingtest --dry-run Error: The 'search-replace' command has been disabled from the config file.

Preparing the new location

Copy all the files from $MAIN_HTML_DIRECTORY to $TEST_HTML_DIRECTORY :

$ cp -R $MAIN_HTML_DIRECTORY/. $TEST_HTML_DIRECTORY/

We’ll need to point the test configuration to the second database. For this we assume the username and password remain the same, but if they are different please change those as well.

Edit the file $TEST_HTML_DIRECTORY/wp-config.php and alter the define('DB_NAME', 'website'); setting.

For those looking to script everything, here is how I do it with sed on the command line:

$ sed -i "s/define('DB_NAME', 'website');/define('DB_NAME', 'test_website');/g" $TEST_HTML_DIRECTORY/wp-config.php

You can verify that the DB settings are correct by standing in the $TEST_HTML_DIRECTORY and running wp db query to view the database.

$ wp db query "show tables;"

There should be no errors and no output. If you get a list of tables it means you’re either connecting to the wrong database or the database is not empty.

Tip: If you need to empty the database (for example to clear out the last staging setup) you can use the wp db reset command:

$ wp db reset --yes Success: Database reset.

We are now ready to load the database in our testing site. In order to do this, we’re going to use wp db import .

$ wp db import ~/db-export.sql Success: Imported from /home/website/db-export.sql

In our reasonably stock WordPress there should be few tables, if we rerun the show tables query again we get the following now:

$ wp db query "show tables;" +------------------------+ | Tables_in_test_website | +------------------------+ | wp_commentmeta | | wp_comments | | wp_links | | wp_options | | wp_postmeta | | wp_posts | | wp_term_relationships | | wp_term_taxonomy | | wp_terms | | wp_usermeta | | wp_users | +------------------------+

If it’s successful you now have a perfect clone of your website. Unfortunately since all the URL’s point to the old location, visiting your site will generally make you go from test.website.com back to website.com .

This is where the wp search-replace command comes in handy. If you took the precaution as outlined before, the search-replace command will fail in both directories, so please remove the $TEST_HTML_DIRECTORY/wp-cli.yml file.

$ rm $TEST_HTML_DIRECTORY/wp-cli.yml $ wp search-replace website.com test.website.com --dry-run --network +----------------+--------------+--------------+ | Table | Column | Replacements | +----------------+--------------+--------------+ | wp_commentmeta | meta_key | 0 | | wp_commentmeta | meta_value | 0 | ... | wp_options | option_value | 4 | +----------------+--------------+--------------+

There are plenty more references, but it’s important to note that some fields will be changing. The majority will not, however.

Let’s break this down. The wp search-replace command will replace all references of the old URL to the new one in the database, while still preserving PHP serialization. The --dry-run command merely checks what will be changed, but does not change anything. The --network option allows this to work for Multisite installs and also catches changes outside the core tables of WordPress (I’ve found this to be the only way to properly move some sites, due to plugins).

Normally it is recommended not the change the GUID entries as they are supposed to remain static for things like RSS readers. If you need to follow this advice you can exclude the column using the extra parameter: --skip-columns=guid . In our case, it should not be a problem as it is used for internal tests.

If the list of changes looks alright (it’s usually massive) then you can execute the command again without --dry-run .

$ wp search-replace website.com test.website.com --network ... Success: Made 8 replacements.

After this change the site should be functional at http://test.website.com !

These instructions should also work when moving to a different host, or changing the domain name.

Barry van Someren is a Java developer and Linux system administrator who fell in love with WordPress because it empowers people to make the most of their online presence. Currently a sysadmin for hire, Barry runs a small WordPress oriented business called CoffeeSprout that offers WordPress hosting and solutions to the Netherlands. You can find Barry on twitter as @bvansomeren.