How to effortlessly move a WordPress site using bash scripting + rsync + WP-CLI

After recently being tasked with moving around 100 sites (some multisite, some not), I thought it worth the effort to create migration scripts.  Now, instead of potentially hours worth of work, running these scripts typically takes less than 1 minute.

I chose to split the scripts up, one to place on the source server, and one on the destination server, but you wouldn’t need to do that if you had ssh keys / everything else set properly.

In any case, here is the source file which I run first.  This script’s job is to export the WordPress db, and then to push the WordPress files, including the db, over to the destination server.

				
					#!/bin/bash

# Filename: src.sh
# ASSUMPTIONS
# Shell access to the source and destination servers and some basic familiarity with bash scripting
# WP-CLI installed on both servers.  https://pagely.com/blog/what-is-wp-cli/
# rsync installed on both servers
# you have the appropriate SSH keys set up to facilitate communication between the servers.  Eg, https://winscp.net/eng/docs/public_key#generate

# DIRECTIONS
# Place this bash script on your source server, eg, naming it source.sh
# Change all vars below according to your needs
# Execute the script (eg, using Putty) from the source server.  Eg: bash -x src.sh 

# Note: don't have privs to make the wp-cli file executable on this server, so i have to use php wp-cli.phar

# DEFINE VARS - Source
source_path_to_wordpress=PATH_TO_WORDPRESS
source_gzip_db=export.sql.gz

# DEFINE VARS - Destination
destination_path_to_wordpress=PATH_TO_WORDPRESS
destination_ssh_user=USER
destination_ssh_host=DESTINATION_HOST


# Dump the source's database contents into a gunzipped file
if $(php wp-cli.phar core is-installed --path=$source_path_to_wordpress); then
	php wp-cli.phar db export --path=$source_path_to_wordpress - | gzip > $source_path_to_wordpress$source_gzip_db
fi

# Rsync files, including gzipped db from source -> destination, excluding hidden files and an updraft backup directory.  Add -dry-run flag if you want to check everything first
if $(php wp-cli.phar core is-installed --path=$source_path_to_wordpress); then
	rsync -avPhi ssh --delete --progress --ignore-errors --exclude='.*' --exclude='wp-config.php' --exclude="cache" --exclude="0-worker.php" --exclude="php-errors.log"  --exclude="nginx-helper" --exclude="worker" --exclude="ws-azuread" $source_path_to_wordpress $destination_ssh_user@$destination_ssh_host:$destination_path_to_wordpress
fi

# Delete exported db since it's imported now 
rm $source_path_to_wordpress$source_gzip_db
				
			

Next up, I run the script using the command bash -x dest.sh at the destination server.  Its job is to first backup the destination WordPress db, then drop all of the existing tables, then import the tables from the source installation, replace production urls with dev urls, flush the cache, then delete the exported db.   

				
					#!/bin/bash

# Filename: dest.sh.  This script was inspired by https://www.benmarshall.me/shell-script-to-sync-local-with-remote-wordpress-sites/

# ASSUMPTIONS
# Shell access to the source and destination servers and some basic familiarity with bash scripting
# WP-CLI installed on both servers.  https://pagely.com/blog/what-is-wp-cli/
# rsync installed on both servers
# you have the appropriate SSH keys set up to facilitate communication between the servers.  Eg, https://winscp.net/eng/docs/public_key#generate

# DIRECTIONS
# Install WP-CLI if you haven't already, referencing https://make.wordpress.org/cli/handbook/guides/installing/.  
# Place this bash script on your destination server, eg, naming it migrate.sh
# Change all vars below according to your needs
# Execute the script (eg, using Putty) from the source server.  Eg: bash -x dest.sh 

# DEFINE VARS

# Source
source_url=csurec.colostate.edu
source_gzip_db=export.sql.gz

# Destination
destination_url=csurec.dev.colostate.edu
destination_db_host=localhost
destination_db_user=XXX
destination_db_pass=XXX
destination_db_name=XXX
destination_path_to_wordpress=XXX
destination_sql_backup=XXX
backup_file=$(date +%Y-%m-%d)


# Backup destination db
if $(wp core is-installed ); then
	wp db export  - | gzip > $destination_sql_backup/$backup_file.sql.gz
fi

# Drop tables from existing db. 
if $(wp core is-installed ); then
	wp db clean --yes 
fi

# Import db that was copied from destination
gunzip -c $destination_path_to_wordpress$source_gzip_db | mysql -u $destination_db_user -p$destination_db_pass $destination_db_name

# Replace URLs in database
if $(wp --url=$source_url core is-installed --network); then
    wp search-replace --network $source_url $destination_url --url=$source_url 
else
    wp search-replace $source_url $destination_url --recurse-objects 
fi

# Activate plugins required by Web Comm
if $(wp --url=$source_url core is-installed --network); then
	wp plugin activate ws-azuread --network 
	wp plugin activate nginx-helper --network 
	wp plugin activate worker --network
else
	wp plugin activate ws-azuread
	wp plugin activate nginx-helper
	wp plugin activate worker
fi

# Deactivate NADI, then delete unused plugins
wp plugin deactivate next-active-directory-integration 
wp plugin delete $(wp plugin list --status=inactive --field=name) 

# Flush cache
if $(wp core is-installed ); then
	wp cache flush 
fi

# Delete exported db since it's imported now 
rm $destination_path_to_wordpress$source_gzip_db