A common feature of managed WordPress hosts is an easy one click deploy to a staging environment. These deployment features are well used. On occasion I’ll have my own customers reach out because they want to clone an existing websites but keep the current production and staging environments as-is. That’s easy, right? I’ll start by making a one time backup snapshot then migrate that over to a new site.

At this point in the conversation the customer asks about the one click deployment option. They don’t just want a clone of an existing environment what they really want is a one click deployment button from anywhere to anywhere. Well with a little scripting and SSH, that possible. You can deploy from any WordPress site to any WordPress site using copy-site.sh
.
I’ve talked about migrating between servers using SSH before however the following copy-site.sh
script is a much simplified version as it utilizes _do
CLI to handle the migration part. In order to use the script for your sites, there are a few things you’ll need to change. Under configurations update SOURCE_SSH
, DEST_SSH
, SOURCE_URL
, DEST_URL
, SOURCE_PATH
and DEST_PATH
. Technically SOURCE_URL and DEST_URL are optional and only used for feedback purposes.
If you haven’t used SSH before, here are a few things you’ll need:
- Generate a key/pair to use. Refer to https://kinsta.com/docs/wordpress-hosting/connect-to-ssh/#how-to-generate-ssh-key-pair-on-mac-linux. I recommend not using a passphrase so that you can keep everything automated.
- Add your public key to your host provider or web server. With Kinsta that by added to your account here: https://my.kinsta.com/profile/myAccount.
Next add the following script to your computer. You can put it anywhere you’d like. I suggest putting it under ~/Scripts/copy-site.sh
. After the file is created you’ll need to grant it execute permission chmod +x ~/Scripts/copy-site.sh
.
#!/bin/bash
set -e
# --- Configuration ---
SOURCE_SSH="username@source-ip-address -p port-number"
DEST_SSH="username@destination-ip-address -p port-number"
SOURCE_URL="https://source-website.tld"
DEST_URL="https://destination-website.tld"
SOURCE_PATH="public"
DEST_PATH="public"
CAPTAINCORE_DO_SCRIPT="curl -sL https://captaincore.io/do | bash -s --"
# --- UI/Logging Functions ---
# Add some color for better readability
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# A function for clear, step-by-step logging
log_step() {
printf "\n${YELLOW}➡️ %s${NC}\n" "$1"
}
log_success() {
printf "${GREEN}✅ %s${NC}\n" "$1"
}
log_error() {
printf "${RED}❌ ERROR: %s${NC}\n" "$1" >&2
exit 1
}
# --- Main Script Logic ---
# 1. Generate Backup on Source
log_step "Generating backup for ${SOURCE_URL}..."
backup_url=$(ssh $SOURCE_SSH "${CAPTAINCORE_DO_SCRIPT} backup $SOURCE_PATH --quiet")
# Validate the backup URL before proceeding
if [[ -z "$backup_url" || ! "$backup_url" == *.zip ]]; then
log_error "Failed to generate backup or received an invalid backup URL."
fi
log_success "Backup created successfully: ${backup_url}"
# Extract filename for cleanup later
filename="${backup_url##*/}"
# 2. Restore Backup to Destination
log_step "Restoring backup to ${DEST_URL}..."
ssh $DEST_SSH "cd $DEST_PATH && ${CAPTAINCORE_DO_SCRIPT} migrate --url=${backup_url} --update-urls"
log_success "Restore complete."
# 3. Cleanup Backup File on Source
log_step "Cleaning up backup file on ${SOURCE_URL}..."
ssh $SOURCE_SSH "rm -f $SOURCE_PATH/${filename}"
log_success "Cleanup complete. Removed ${filename} from source server."
# 4. Final Confirmation
log_step "✨ All done! Your site is ready."
log_success "URL: ${DEST_URL}"
Very efficient and offloads the heavy lifting to the web servers.
This script could easily be run on a cell phone as no files are actually downloaded or uploaded. The only thing that is required is time to let the source server complete a full site backup and for the destination server to download and perform the migration. It’s helpful to increase SSH’s timeout time on the computer that is running the script. Here are recommend settings for ~/.ssh/config
.
Host *
ServerAliveInterval 240
StrictHostKeyChecking no
ConnectTimeout 15
After running site-copy.ssh
here is an example of what you should see: https://app.warp.dev/block/fQe6oXLmTZAsQAbIXZECSP. The migration script hasn’t been tested with all host providers so if you spot an issue feel free to post about it on Github: https://github.com/CaptainCore/do.