← Back to Home

WordPress Database Migration: 3 Real Traps and Fixes

WordPressdatabase migrationwp-cli

WP-CLI's search-replace command handles serialized data length fields automatically. This is the correct approach:

# Dry-run first to see the impact
wp search-replace 'old.example.com' 'new.example.com' --all-tables --dry-run --report-changed-only

# Execute after confirming
wp search-replace 'old.example.com' 'new.example.com' --all-tables

Don't skip these two flags:

If your fields contain JSON (WordPress 5.7+ stores some wp_usermeta fields in JSON), add --precise:

wp search-replace 'old.example.com' 'new.example.com' --all-tables --precise

--precise uses PHP's preg_replace per row instead of MySQL's REPLACE(). It's 3-5x slower but safer for serialized and JSON data.

Collation Mismatch: The Silent Bomb When Moving from MySQL 5.7 to 8.0

MySQL 8.0 changed the default collation from utf8mb4_unicode_ci to utf8mb4_0900_ai_ci. When you import a 5.7 database dump into 8.0, the SQL dump's COLLATE=utf8mb4_unicode_ci mixes with 8.0's default utf8mb4_0900_ai_ci, and JOIN queries fail:

Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and (utf8mb4_unicode_ci,IMPLICIT) for operation '='

This error sits quietly in the background. The front-end symptom: some pages fail to load, WooCommerce order queries break, search stops working.

Fix: Unify collation before importing—not after. Fixing post-import requires ALTER on every table and column, too easy to miss one.

# Replace collation in the dump file before importing
mysqldump -u root -p wordpress_db > dump.sql
sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' dump.sql
# Or the other direction, unifying to 0900
# sed -i 's/utf8mb4_unicode_ci/utf8mb4_0900_ai_ci/g' dump.sql

Which direction? **Follow the target server's MySQL version**. Target runs 8.0? Unify to utf8mb4_0900_ai_ci. Target runs 5.7? Unify to utf8mb4_unicode_ci.

If you already imported and are hitting errors, batch-fix tables:

# Generate ALTER statements for all mismatched tables
mysql -u root -p -e "
SELECT CONCAT('ALTER TABLE \`', TABLE_NAME, '\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;')
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'wordpress_db'
AND TABLE_COLLATION != 'utf8mb4_unicode_ci';" > fix_collation.sql

# Execute
mysql -u root -p wordpress_db < fix_collation.sql

Three Hidden WP-CLI search-replace Omissions

Omission 1: Skipping tables with custom prefixes

If your wp-config.php has a non-default $table_prefix (e.g., wp_42_), wp search-replace only processes tables registered in $wpdb. Third-party plugin tables aren't automatically included.

# Wrong: only replaces default registered tables
wp search-replace 'old.example.com' 'new.example.com'

# Correct: covers all tables
wp search-replace 'old.example.com' 'new.example.com' --all-tables

# Multisite
wp search-replace 'old.example.com' 'new.example.com' --network --all-tables

Omission 2: You shouldn't replace the GUID field

The guid column in wp_posts stores each post's permanent identifier. WordPress documentation explicitly states that guids should not change after publication. Replacing guids causes RSS readers to re-push all old articles to subscribers.

# Exclude guid column
wp search-replace 'old.example.com' 'new.example.com' --all-tables --skip-columns=guid

Omission 3: Memory exhaustion on large databases

When the database exceeds 1GB, wp search-replace can hit PHP memory limits and abort. The correct approach is to process tables individually:

# Process table by table, avoid full-scan OOM
wp search-replace 'old.example.com' 'new.example.com' wp_options --dry-run
wp search-replace 'old.example.com' 'new.example.com' wp_postmeta --dry-run
wp search-replace 'old.example.com' 'new.example.com' wp_posts --dry-run
# Confirm each table, then remove --dry-run to execute

Or raise the PHP memory limit directly:

wp search-replace 'old.example.com' 'new.example.com' --all-tables --skip-columns=guid --memory-limit=512M

Pre- and Post-Migration Checklist

Before migration:

1. Backup the target database (mysqldump full backup)

2. Check MySQL version and collation on both source and target

3. Run wp search-replace --dry-run to estimate affected rows

After migration:

# 1. Verify homepage responds
curl -sI https://new.example.com | head -5

# 2. Check for residual old domain references
wp db query "SELECT COUNT(*) FROM wp_options WHERE option_value LIKE '%old.example.com%'"

# 3. Flush cache and rewrite rules
wp cache flush
wp rewrite flush

# 4. Check for collation mixing
wp db query "SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_COLLATION != 'utf8mb4_unicode_ci'" 2>/dev/null

For more WordPress operations guidance, see my earlier posts on WordPress Security Hardening 2026 and Cloudflare Tunnel Zero-Port Setup. Database migration is a core operations skill that goes hand-in-hand with security and performance work.

This post contains affiliate links. If you purchase through these links, I may earn a commission at no extra cost to you.

📌 This article was AI-assisted generated and human-reviewed | TechPassive — An AI-driven content testing site focused on real tool reviews

🔗 Recommended Tools

These are carefully selected tools. Using our affiliate links supports us to keep producing quality content:

☁️ DigitalOcean Cloud ⚡ Vultr VPS 📚 WordPress Books 🔍 WordPress SEO Books 🌐 Web Hosting Books 🐳 Docker Books 🐧 Linux Books 🐍 Python Books 💰 Affiliate Marketing 💵 Passive Income Books 🖥️ Server Books ☁️ Cloud Computing Books 🚀 DevOps Books ⭐ MiniMax Token Plan 🔍 Cloud Search
← Back to Home