Cloning a WordPress site using the same database

A simple and convenient method to create a clone of an existing WordPress site over SSH using WP-CLI, make some major overhauls to it while the original site is still functioning as normal, and once the work is completed replace the original with the clone.

Skip to just the commands

This is not a live <-> staging <-> dev workflow, where changes can be pushed from one environment to another, this process creates a clone (a fork) of the live site and later replaces the live site with the clone (although that part is optional).

Both sites will be housed in the same database (using a different table prefix) to have as few moving parts as possible – no need to create another database, no third party plugins.

Useful when switching to a completely different theme, experimenting with a complex plugin or something like that.
Not useful if the site you’re dealing with is, for example, a high traffic WooCommerce store that receives frequent orders – you’ll lose the orders and stock status when restoring the clone.

Requirements

  • SSH access to a UNIX-based server
  • WP-CLI available on the server

Not ideal for

  • Sites with a massive database (you’ll be doubling its’ size for a while)
  • Sites with gigabytes of media (you’ll be duplicating everything)
  • Sites with frequent data updates (if you’ll be later replacing the live site with the clone changes on live would be lost)

Creating a clone

On the websever navigate to the public document root, be it htdocs, public_html , www or something else:

Bash
cd htdocs

Create a snapshot of the current database:

Bash
wp db export

This will generate a .sql file with an auto-generated name, for the purposes of this guide let’s call it the-exported-database.sql, but in the terminal simply type in the first letter or 2 of its’ name and press Tab to autocomplete.

Clone the filesystem: the following rsync command takes all the files in the current directory and copies them to a subdirectory in the same folder:

Bash
rsync -Rr --progress ./ ./site-clone

-R – preserve the relative path, -r – recursive. Now you have a copy of the whole filesystem.

Before you leave here, get rid of the database file, as it got cloned as well and the original is no longer needed:

Bash
# Delete the database file
rm -f the-exported-database.sql
# or move it out of the public directory
mv the-exported-database.sql ..

Now to work on the clone:

Bash
cd site-clone

Instead of creating a new database and updating the credentials it’s more convenient to just change the database table prefix:

PHP
wp config get table_prefix
> wp_

wp config set table_prefix wp2_
> Success: Updated the variable 'table_prefix' in the 'wp-config.php' file with the value 'wp_clone_'.

Make sure the new prefix doesn’t begin with the old prefix, e.g. wp_ -> wp_clone_ because then both start with wp_ and are removed when using wp db clean command.

The database tables in the exported .sql file still contain the old prefix, so it needs to be updated as well. All the instances of the table prefix should follow the format {space}{uptick}{prefix} which makes it possible to change with string replacements:

...
--
-- Table structure for table `wp_postmeta`
--

DROP TABLE IF EXISTS `wp_postmeta`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `wp_postmeta` (
  `meta_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
...

There is no WP-CLI command to handle that (wp search-replace wouldn’t work), how ever GitHub user austinginder provided easy sed commands that can do string replacements on the database file in an issue wp-cli/db-command/issues/161:

Update table prefixesPHP
sed -i 's# `wp_# `wp2_#g' the-exported-database.sql

3 more things to update – the option name {prefix}_user_roles depends on the database prefix, as well as 2 user meta keys: {prefix}_capabilities and {prefix}_user_level. Update them with the following sed commands:

PHP
sed -i 's#'wp_user_roles'#'wp2_user_roles'#g' the-exported-database.sql
sed -i 's#'wp_capabilities'#'wp2_capabilities'#g' the-exported-database.sql
sed -i 's#'wp_user_level'#'wp2_user_level'#g' the-exported-database.sql

And then import the database:

Importing the databaseBash
wp db import the-exported-database.sql

> Success: Imported from 'the-exported-database.sql'.

Modify the imported data to work with the cloned site:

Dry runBash
wp search-replace 'example.com' 'example.com/site-clone' --dry-run
> ...
> Success: 1411 replacements to be made.

The above code will update all the URLs in the database to work in the new subdirectory. First you can run the command with the --dry-run to check that everything is in order, if all looks good (there are changes to be made) then run it for real.

Make the replacementsBash
wp search-replace 'example.com' 'example.com/site-clone'

If your site also contained instances of e-mail addresses using the domain, then you might want to fix those:

Fix e-mailsBash
wp search-replace '@example.com/clone' '@example.com' --dry-run

This command fixes instances when the earlier replacements changed an e-mail e.g. info@example.com to info@example.com/site-clone which is not a valid e-mail. We could have avoided doing that in the first place, but that would have taken multiple, more specific replacements, checking for leading http(s)://(www). and that would have been more time consuming.

Check the results:

Verify the site URLBash
wp option get siteurl

If the following command outputs https://example.com/site-clone rather than https://example.com then the replacements were probably successful and you can head on over to https://example.com/site-clone/wp-admin/ to visit the admin dashboard of the cloned site – your user credentials are the same as the original site.

After moving a site, you usually also need to flush rewrite rules:

wp rewrite flush --hard

This command doesn’t always work, requires special WP-CLI configuration.
Simply going to wp-admin -> Settings -> Permalinks and hitting “Save Changes” works just as well.

Replacing the live site with the clone

Once you’ve made changes to the clone and are happy with it, it’s time to get rid of the live site and have the clone take its’ place.

There are multiple ways to approach this, but the method with the least cognitive overhead, in my opinion, is to use the following sequence:

  • Move the clone directory (up) out of the public directory
  • Rename the public directory to something else
  • Rename the clone directory to the public directory

This assumes you have access to one directory level higher than the public directory (public_html, htdocs, www…) , which some hosting providers may not provide.

In commands:

Replace live site with clone siteBash
# Assuming you're in the root directory of the clone:
pwd
> /var/www/public_html/site-clone

# Move upwards in the filesystem:
cd ..
pwd
> /var/www/public_html

# OPTIONAL - remove live site's database tables
wp db clean

# Move the clone's directory out:
mv site-clone ..

# Move to the parent directory
cd ..

# Rename the public directory:
mv public_html old_public_html

# Rename the clone to be the new public web root directory:
mv site-clone public_html

Simple as that, the old site is now sitting in old_public_html, do with it what you will. Let’s make the new site functional:

Update databaseBash
cd public_html

# Update the URLs in the database
wp search-replace 'example.com/site-clone' 'example.com'

All done. If you want, you can archive the old site:

Archiving the old siteBash
# If you kept the original database file
mv the-exported-database.sql old_public_html

# Zip it up
zip -r -q old-site.zip old_public_html

# Remove it, leaving only the .zip around
rm -rf old_public_html

TL;DR – just the commands:

Run these commands in WordPress root directory and make note of the table prefix and exported database file name:

wp config get table_prefix
wp db export
Input your data to personalize the commands:

Create a clone

1. Clone the filesystemBash
rsync -Rr ./ ./{dir}
2. Remove the database fileBash
rm -f {sql}
3. Navigate to the clone directoryBash
cd {dir}
4. Update table prefix in wp-config.phpBash
wp config set table_prefix {p2}
5. Update database table prefixesBash
sed -i 's# `{p1}# `{p2}#g' {sql}
6. Update user roles option nameBash
sed -i 's#'{p1}user_roles'#'{p2}user_roles'#g' {sql}
7. Update user capabilities meta keyBash
sed -i 's#'{p1}capabilities'#'{p2}capabilities'#g' {sql}
8. Update user level meta keyBash
sed -i 's#'{p1}user_level'#'{p2}user_level'#g' {sql}
9. Import database for the cloneBash
wp db import {sql}
10. Update URLsBash
wp search-replace '{domain}' '{domain}/{dir}'

Replace original with clone

Run these commands starting in the web root directory of the live site:

1. Remove live site’s tables from the databaseBash
wp db clean
2. Move the clone’s directory outBash
mv {dir} ..
3. Move upwards in the filesystemBash
cd ..
4. Rename the public web root directory to something elseBash
mv {pub} old_{pub}
5. Rename the clone to be the new public web root directoryBash
mv {dir} {pub}
6. Navigate to the public web root directoryBash
cd {pub}
7. Update URLs in the databaseBash
wp search-replace '{domain}/{dir}' '{domain}'

You’ll probably also need to flush rewrite rules.

Leave a Reply

Your email address will not be published. Required fields are marked *