Cloning WordPress sites manually means exporting databases through phpMyAdmin, FTPing thousands of files, running search-replace SQL queries that break serialized data, and spending hours fixing broken configurations. One mistake destroys the entire clone.
WP-CLI transforms WordPress cloning and migration into a fast, automated process—clone production to staging in minutes, migrate sites between hosts reliably, and replicate environments for testing without manual intervention or data corruption.
In this guide, you’ll build a complete WordPress site cloning and migration system using WP-CLI, with automated scripts, safety checks, and verification procedures used by professional WordPress agencies.
Why Automate WordPress Cloning and Migration?
WordPress site migration manually is error-prone and doesn’t scale.
Problems with Manual Cloning/Migration
Time-consuming: Manual cloning takes 2-4 hours for medium sites.
Error-prone: Database exports fail, files get missed, URLs break.
Serialized data corruption: Manual search-replace destroys widget settings and options.
No validation: Can’t verify clone completeness or data integrity.
Not repeatable: Each clone requires same tedious manual steps.
WP-CLI Cloning/Migration Advantages
Fast: Clone complete sites in 5-10 minutes instead of hours.
Safe: WordPress-aware operations preserve serialized data.
Scriptable: One-command cloning for any site at any time.
Verifiable: Automated checks ensure complete, accurate clones.
Consistent: Same process every time guarantees reliability.
According to WordPress development surveys, agencies save 20+ hours monthly by automating site cloning for staging and development.
Basic Site Cloning
Clone a WordPress site locally with essential components.
Complete Local Clone Script
#!/bin/bash
# clone-wordpress-site.sh - Clone WordPress site locally
set -euo pipefail
SOURCE_PATH="$1"
DEST_PATH="$2"
if [ -z "$SOURCE_PATH" ] || [ -z "$DEST_PATH" ]; then
echo "Usage: $0 <source_path> <dest_path>"
exit 1
fi
echo "Cloning WordPress site..."
echo "Source: $SOURCE_PATH"
echo "Destination: $DEST_PATH"
# Create destination directory
mkdir -p "$DEST_PATH"
# Copy WordPress files
echo "Copying files..."
rsync -av --exclude='wp-content/cache' \
--exclude='wp-content/backup*' \
"$SOURCE_PATH/" "$DEST_PATH/"
# Export database from source
echo "Exporting database..."
cd "$SOURCE_PATH"
wp db export /tmp/clone-db.sql
# Create new database config for destination
cd "$DEST_PATH"
# Get source database details
SOURCE_DB=$(wp config get DB_NAME)
SOURCE_USER=$(wp config get DB_USER)
SOURCE_PASS=$(wp config get DB_PASSWORD)
# Create new database
NEW_DB="cloned_${SOURCE_DB}"
mysql -u"$SOURCE_USER" -p"$SOURCE_PASS" -e "CREATE DATABASE IF NOT EXISTS ${NEW_DB};"
# Update wp-config.php
wp config set DB_NAME "$NEW_DB"
# Import database
echo "Importing database..."
wp db import /tmp/clone-db.sql
# Cleanup
rm /tmp/clone-db.sql
# Update URLs if needed
SOURCE_URL=$(wp option get siteurl)
echo "Current site URL: $SOURCE_URL"
read -p "Enter new site URL (or press Enter to keep same): " NEW_URL
if [ -n "$NEW_URL" ]; then
echo "Updating URLs..."
wp search-replace "$SOURCE_URL" "$NEW_URL"
wp option update home "$NEW_URL"
wp option update siteurl "$NEW_URL"
fi
# Clear caches
wp cache flush
wp rewrite flush
echo "✓ Clone complete: $DEST_PATH"Run it:
chmod +x clone-wordpress-site.sh
./clone-wordpress-site.sh /var/www/production /var/www/stagingLearn about WordPress directory structure.
Remote Site Migration
Migrate WordPress sites between servers or hosting providers.
Complete Remote Migration Script
#!/bin/bash
# migrate-wordpress-remote.sh - Migrate between servers
set -euo pipefail
SOURCE_HOST="$1"
SOURCE_PATH="$2"
DEST_PATH="$3"
OLD_URL="$4"
NEW_URL="$5"
if [ -z "$NEW_URL" ]; then
echo "Usage: $0 <source_host> <source_path> <dest_path> <old_url> <new_url>"
echo "Example: $0 user@old-server.com /var/www/html /var/www/html https://oldsite.com https://newsite.com"
exit 1
fi
TEMP_DIR="/tmp/wp-migration-$"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$TEMP_DIR"
echo "=== WordPress Remote Migration Started ==="
echo "From: $SOURCE_HOST:$SOURCE_PATH"
echo "To: $DEST_PATH"
echo "URL: $OLD_URL → $NEW_URL"
# Step 1: Export database from source
echo "Step 1/6: Exporting database from source..."
ssh "$SOURCE_HOST" "cd $SOURCE_PATH && wp db export - | gzip" > "$TEMP_DIR/database.sql.gz"
# Verify database export
DB_SIZE=$(stat -c%s "$TEMP_DIR/database.sql.gz")
if [ "$DB_SIZE" -lt 1000 ]; then
echo "✗ Database export too small, aborting"
exit 1
fi
# Step 2: Transfer files from source
echo "Step 2/6: Transferring files from source..."
rsync -avz --progress \
--exclude='wp-content/cache/*' \
--exclude='wp-content/backup*' \
"$SOURCE_HOST:$SOURCE_PATH/" "$DEST_PATH/"
# Step 3: Backup destination (if exists)
if [ -d "$DEST_PATH" ]; then
echo "Step 3/6: Backing up destination..."
cd "$DEST_PATH"
if wp core is-installed 2>/dev/null; then
wp db export "/backups/dest-before-migration-$DATE.sql.gz" || true
fi
fi
# Step 4: Import database
echo "Step 4/6: Importing database..."
cd "$DEST_PATH"
wp db import "$TEMP_DIR/database.sql.gz"
# Step 5: Update URLs
echo "Step 5/6: Updating URLs..."
wp search-replace "$OLD_URL" "$NEW_URL" --dry-run --report
wp search-replace "$OLD_URL" "$NEW_URL"
wp search-replace "//$(echo $OLD_URL | sed 's~http[s]*://~~')" "//$(echo $NEW_URL | sed 's~http[s]*://~~')"
# Update WordPress options
wp option update home "$NEW_URL"
wp option update siteurl "$NEW_URL"
# Step 6: Verify and cleanup
echo "Step 6/6: Verifying migration..."
if wp core is-installed; then
echo "✓ WordPress installation verified"
else
echo "✗ WordPress verification failed"
exit 1
fi
if wp db check; then
echo "✓ Database healthy"
else
echo "✗ Database issues detected"
exit 1
fi
# Clear caches
wp cache flush
wp rewrite flush
# Cleanup
rm -rf "$TEMP_DIR"
echo "=== Migration Complete ==="
echo "Site URL: $NEW_URL"
echo "Backup: /backups/dest-before-migration-$DATE.sql.gz"Push to Production
#!/bin/bash
# push-to-production.sh - Push staging to production
STAGING_PATH="/var/www/staging"
PROD_PATH="/var/www/production"
STAGING_URL="https://staging.example.com"
PROD_URL="https://example.com"
read -p "This will overwrite production. Continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0
fi
# Backup production first
cd "$PROD_PATH"
echo "Backing up production..."
wp db export "/backups/prod-$(date +%Y%m%d_%H%M%S).sql.gz"
# Export from staging
cd "$STAGING_PATH"
echo "Exporting staging..."
wp db export /tmp/staging-push.sql.gz
# Copy files
echo "Copying files..."
rsync -av --delete \
--exclude='wp-config.php' \
"$STAGING_PATH/" "$PROD_PATH/"
# Import to production
cd "$PROD_PATH"
echo "Importing database..."
wp db import /tmp/staging-push.sql.gz
# Update URLs
echo "Updating URLs for production..."
wp search-replace "$STAGING_URL" "$PROD_URL"
wp option update home "$PROD_URL"
wp option update siteurl "$PROD_URL"
# Clear caches
wp cache flush
wp rewrite flush
# Cleanup
rm /tmp/staging-push.sql.gz
echo "✓ Staging pushed to production"Advanced Cloning Features
Add professional features to cloning automation.
Selective Cloning
#!/bin/bash
# selective-clone.sh - Clone only specific components
SOURCE_PATH="$1"
DEST_PATH="$2"
CLONE_TYPE="$3" # database, files, plugins, themes, uploads
case "$CLONE_TYPE" in
database)
echo "Cloning database only..."
cd "$SOURCE_PATH"
wp db export /tmp/db-clone.sql.gz
cd "$DEST_PATH"
wp db import /tmp/db-clone.sql.gz
rm /tmp/db-clone.sql.gz
;;
files)
echo "Cloning files only..."
rsync -av "$SOURCE_PATH/" "$DEST_PATH/"
;;
plugins)
echo "Cloning plugins..."
rsync -av "$SOURCE_PATH/wp-content/plugins/" "$DEST_PATH/wp-content/plugins/"
;;
themes)
echo "Cloning themes..."
rsync -av "$SOURCE_PATH/wp-content/themes/" "$DEST_PATH/wp-content/themes/"
;;
uploads)
echo "Cloning uploads..."
rsync -av "$SOURCE_PATH/wp-content/uploads/" "$DEST_PATH/wp-content/uploads/"
;;
*)
echo "Usage: $0 <source> <dest> <database|files|plugins|themes|uploads>"
exit 1
;;
esac
echo "✓ Selective clone complete"Clone with Sanitization
#!/bin/bash
# clone-sanitized.sh - Clone for development, remove sensitive data
SOURCE_PATH="$1"
DEST_PATH="$2"
# Clone site
bash /usr/local/bin/clone-wordpress-site.sh "$SOURCE_PATH" "$DEST_PATH"
cd "$DEST_PATH"
echo "Sanitizing clone for development..."
# Deactivate security plugins
wp plugin deactivate wordfence sucuri-scanner --quiet
# Remove sensitive data
echo "Removing sensitive user data..."
# Anonymize user emails
for user_id in $(wp user list --field=ID); do
USERNAME=$(wp user get $user_id --field=user_login)
wp user update $user_id --user_email="${USERNAME}@example.test"
done
# Remove real passwords (force password reset)
for user_id in $(wp user list --field=ID); do
wp user update $user_id --user_pass="development"
done
# Remove payment gateway credentials
wp option update woocommerce_stripe_settings '{"enabled":"no"}'
wp option update woocommerce_paypal_settings '{"enabled":"no"}'
# Disable external services
wp plugin deactivate google-analytics mailchimp --quiet
# Enable debug mode
wp config set WP_DEBUG true --raw
wp config set WP_DEBUG_LOG true --raw
echo "✓ Clone sanitized for development"Multi-Environment Sync
Synchronize WordPress across development, staging, and production.
Environment Sync System
#!/bin/bash
# sync-environments.sh - Sync between dev/staging/prod
OPERATION="$1" # pull or push
ENV_FROM="$2" # dev, staging, prod
ENV_TO="$3" # dev, staging, prod
# Environment paths
declare -A PATHS=(
["dev"]="/var/www/dev"
["staging"]="/var/www/staging"
["prod"]="/var/www/production"
)
# Environment URLs
declare -A URLS=(
["dev"]="http://dev.local"
["staging"]="https://staging.example.com"
["prod"]="https://example.com"
)
if [ -z "$ENV_TO" ]; then
echo "Usage: $0 <pull|push> <from_env> <to_env>"
echo "Environments: dev, staging, prod"
exit 1
fi
SOURCE_PATH="${PATHS[$ENV_FROM]}"
DEST_PATH="${PATHS[$ENV_TO]}"
SOURCE_URL="${URLS[$ENV_FROM]}"
DEST_URL="${URLS[$ENV_TO]}"
echo "Syncing $ENV_FROM → $ENV_TO"
# Confirmation for production
if [ "$ENV_TO" == "prod" ]; then
read -p "WARNING: Syncing to production. Continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0
fi
fi
# Backup destination
cd "$DEST_PATH"
echo "Backing up $ENV_TO..."
wp db export "/backups/${ENV_TO}-before-sync-$(date +%Y%m%d).sql.gz"
# Clone
cd "$SOURCE_PATH"
wp db export /tmp/env-sync.sql.gz
rsync -av \
--exclude='wp-config.php' \
--exclude='wp-content/cache' \
"$SOURCE_PATH/" "$DEST_PATH/"
cd "$DEST_PATH"
wp db import /tmp/env-sync.sql.gz
# Update URLs
wp search-replace "$SOURCE_URL" "$DEST_URL"
wp option update home "$DEST_URL"
wp option update siteurl "$DEST_URL"
# Clear caches
wp cache flush
wp rewrite flush
rm /tmp/env-sync.sql.gz
echo "✓ Environment sync complete: $ENV_FROM → $ENV_TO"Clone Verification
Validate clones are complete and functional.
Complete Verification Script
#!/bin/bash
# verify-clone.sh - Verify WordPress clone
CLONE_PATH="$1"
if [ -z "$CLONE_PATH" ]; then
echo "Usage: $0 <clone_path>"
exit 1
fi
cd "$CLONE_PATH"
echo "Verifying WordPress clone..."
# Check WordPress is installed
if ! wp core is-installed 2>/dev/null; then
echo "✗ WordPress not properly installed"
exit 1
fi
echo "✓ WordPress installed"
# Check database connection
if ! wp db check 2>/dev/null; then
echo "✗ Database connection failed"
exit 1
fi
echo "✓ Database connected"
# Verify post count matches
# (Would need original count passed in for real comparison)
POST_COUNT=$(wp post list --format=count)
echo "✓ Posts: $POST_COUNT"
# Verify user count
USER_COUNT=$(wp user list --format=count)
echo "✓ Users: $USER_COUNT"
# Verify plugins
PLUGIN_COUNT=$(wp plugin list --format=count)
echo "✓ Plugins: $PLUGIN_COUNT"
# Check for broken URLs
SITE_URL=$(wp option get siteurl)
echo "Site URL: $SITE_URL"
# Test site accessibility
if curl -f -s "$SITE_URL" > /dev/null; then
echo "✓ Site accessible"
else
echo "✗ Site not accessible"
exit 1
fi
# Verify file permissions
if [ -w wp-content ]; then
echo "✓ wp-content writable"
else
echo "✗ wp-content not writable"
fi
echo "✓ Clone verification complete"Next Steps
You now have complete WordPress site cloning and migration automation capabilities.
Recommended Learning Path
Week 1: Basic cloning
- Practice local clones
- Test database operations
- Master URL replacement
Week 2: Remote migrations
- Set up SSH access
- Transfer between servers
- Handle production pushes
Week 3: Advanced features
- Selective cloning
- Data sanitization
- Multi-environment sync
Week 4: Production deployment
- Build verification systems
- Automate complete workflows
- Document procedures
Advanced Topics
- Blue-Green Deployments – Zero-downtime migrations
- Database Sync Strategies – Incremental updates
- Large Site Optimization – Handle massive WordPress sites
Get More Resources
Download cloning scripts including:
- Complete automation system
- Verification tools
- Multi-environment management
- Weekly WP-CLI tutorials
- Migration best practices
- DevOps workflows
Conclusion
WordPress site cloning and migration automation with WP-CLI transforms hours of manual work into fast, reliable, one-command operations you can trust.
What we covered:
✅ Complete local site cloning automation
✅ Remote migration between servers
✅ Advanced selective and sanitized cloning
✅ Multi-environment synchronization
✅ Clone verification and validation
✅ Production-ready workflows
Master these techniques, and you’ll clone WordPress sites effortlessly—whether creating staging environments, migrating to new hosts, or replicating sites for development.
Ready for more? Learn WordPress backup automation or deployment pipelines.
Questions about WordPress cloning and migration? Drop a comment below!
Found this helpful? Share with other WordPress developers and agencies.
