Most WordPress migration guides tell you to install a plugin and click a button. That works until it doesn’t. Your site is too large for the free tier. The plugin chokes on a custom table prefix. Serialized data gets corrupted and your widgets vanish. If you’ve been through any of that, this guide is for you.

This is the developer approach to migrating WordPress. SSH, rsync, mysqldump, WP-CLI, and a DNS strategy that keeps your site live the entire time. No downtime, no data loss, no surprises. If you’re still setting up your first server, start with our guide on how to set up a WordPress server from scratch and come back here when you’re ready to move.

TL;DR

  • Lower your DNS TTL to 300 seconds at least 48 hours before migration. If your current TTL is 86400 (24 hours), changing it the day of migration does nothing because cached records won’t refresh until the old TTL expires.
  • Use rsync for files and mysqldump for the database. Plugins work for small sites, but the command line handles any size without memory limits or timeouts.
  • Never run raw SQL REPLACE on WordPress databases. It corrupts serialized data. Use wp search-replace instead, which recalculates PHP serialized string lengths automatically.
  • Test on the new server using your hosts file before touching DNS. This lets you verify everything works while the old site stays live for visitors.
  • Do a two-sync migration: initial bulk transfer while the old site runs, then a quick delta sync during a brief maintenance window before DNS cutover.

Pre-Migration Checklist

Don’t touch a single file until you’ve done these. Skipping the prep is how migrations turn into disasters.

Full Backup

Take a complete backup of both files and database on the old server. If you have WP-CLI installed, wp db export backup.sql handles the database. For files, tar -czf site-backup.tar.gz /var/www/html creates a compressed archive. Store the backup somewhere separate from both servers. If something breaks during migration, this is your revert point.

Lower DNS TTL (48-72 Hours Before)

This is the step everyone skips and then regrets. Your domain’s TTL (Time to Live) tells DNS resolvers how long to cache your IP address. If your TTL is set to 86400 (24 hours), every resolver that cached your record in the last 24 hours will keep pointing to the old server until their cache expires.

Log into your DNS provider and drop the TTL to 300 (5 minutes). Do this at least 48 hours before migration day. That gives every cached record time to expire under the old TTL. When you finally change the IP, resolvers worldwide will pick up the new address within 5 minutes instead of 24 hours.

Audit Your Current Setup

Record these on the old server before you start. You’ll need them to match the new environment:

  • PHP version (php -v)
  • Active plugins (wp plugin list --status=active)
  • Database table prefix (check wp-config.php for $table_prefix)
  • Any custom wp-config.php constants (memory limits, debug flags, custom paths)
  • Cron jobs (crontab -l)
  • Server software (Apache vs Nginx) and any custom rewrite rules

Method 1: Plugin Migration (Quick Overview)

If your site is under 500MB, a plugin migration is the fastest option. Duplicator, All-in-One WP Migration, and Migrate Guru all handle the export/import process through the WordPress admin. Install the plugin, create a package, download it, upload to the new server, and run the installer.

This breaks down when:

  • Your site exceeds the free tier’s size limit (usually 500MB-1GB)
  • You’re on shared hosting with low PHP memory or execution time limits
  • Your database has a non-standard table prefix
  • You need multisite or custom table support
  • You want zero downtime (plugins require DNS to point to the new server before you can verify anything works)

For anything beyond a basic blog, the command line gives you full control.

Method 2: Manual Migration via SSH

This is the standard developer migration. You’ll need SSH access to both the old and new servers.

Linux terminal command line used for WordPress server migration via SSH

Transfer Files with rsync

rsync is the go-to for server-to-server file transfers. It only copies changed files on subsequent runs, making delta syncs fast. From the new server, pull files from the old one:

rsync -avz --progress \
  --exclude='wp-content/cache/*' \
  --exclude='debug.log' \
  --exclude='node_modules' \
  --exclude='.git' \
  user@old-server:/var/www/html/ /var/www/html/

The -a flag preserves permissions, timestamps, and symlinks. -z compresses data during transfer. --progress shows you what’s happening. The exclude patterns skip cache files and development artifacts that don’t belong on production.

If you’re running SSH on a non-standard port:

rsync -avz -e "ssh -p 2222" user@old-server:/var/www/html/ /var/www/html/

Export and Import the Database

On the old server, dump the database:

mysqldump --single-transaction --routines --triggers \
  --default-character-set=utf8mb4 \
  -u db_user -p db_name | gzip > database.sql.gz

--single-transaction takes a consistent snapshot without locking tables, so your live site keeps running during the dump. --routines and --triggers capture stored procedures and triggers that some plugins create. The gzip pipe compresses the output, which matters when you’re transferring a 500MB database over the network.

Transfer to the new server and import:

scp database.sql.gz user@new-server:~/
# On the new server:
gunzip < database.sql.gz | mysql -u db_user -p db_name

Update wp-config.php

Edit wp-config.php on the new server to match the new database credentials:

define('DB_NAME', 'new_db_name');
define('DB_USER', 'new_db_user');
define('DB_PASSWORD', 'new_db_password');
define('DB_HOST', 'localhost');

Fix File Permissions

After rsync, ownership and permissions often need adjusting for the new server's web user:

# Set ownership (adjust www-data to your web server user)
chown -R www-data:www-data /var/www/html/

# Standard WordPress permissions
find /var/www/html/ -type f -exec chmod 644 {} \;
find /var/www/html/ -type d -exec chmod 755 {} \;

# wp-content needs write access
chmod -R 775 /var/www/html/wp-content/

Method 3: WP-CLI Migration

WP-CLI condenses the entire process into a handful of commands. With it installed on both servers (and you should have it), this is the cleanest path.

Export the Database

# On old server
wp db export migration.sql --single-transaction

Search and Replace URLs

This is the most important command in the migration. wp search-replace handles PHP serialized data correctly, which is the single biggest difference between a clean migration and a broken one.

WordPress stores serialized PHP arrays in the database for widgets, theme settings, plugin options, and more. A serialized string looks like s:23:"http://oldsite.com/path" where s:23 declares the string length. If you swap in a longer domain using raw SQL REPLACE(), the declared length no longer matches the actual string, and PHP can't unserialize the data. Widgets disappear. Theme settings revert. Plugin configurations vanish.

wp search-replace recalculates those lengths automatically:

# Always dry-run first
wp search-replace 'https://oldsite.com' 'https://newsite.com' --all-tables --dry-run

# If the count looks right, run it for real
wp search-replace 'https://oldsite.com' 'https://newsite.com' \
  --all-tables \
  --skip-columns=guid \
  --recurse-objects

Key flags: --all-tables covers custom plugin tables, not just core WordPress tables. --skip-columns=guid preserves RSS feed GUIDs (changing these causes RSS readers to show all posts as new). --recurse-objects handles nested serialized data that some plugins create.

Verify the Migration

# Verify WordPress core files
wp core verify-checksums

# Check active plugins still match
wp plugin list --status=active

# Confirm URLs are correct
wp option get siteurl
wp option get home

# Flush rewrite rules (fixes 404s)
wp rewrite flush

# Check database integrity
wp db check

The Zero-Downtime Strategy

Combining the methods above with a deliberate DNS strategy lets you migrate with zero visitor-facing downtime. Here's the timeline.

48-72 Hours Before: Lower DNS TTL

As covered in the pre-migration checklist, drop your TTL to 300 seconds. The math matters here: if your current TTL is 86400 and you change it at 9 AM Monday, resolvers that cached your record at 8:59 AM won't check again until 8:59 AM Tuesday. You need the old TTL to fully expire across all resolvers before migration day.

Day Of: Initial Bulk Sync

Run rsync and mysqldump while the old site stays live and serving traffic. This initial transfer can take hours for large sites. That's fine. Visitors never notice because DNS still points to the old server.

Test via Hosts File

Before touching DNS, test the new server by editing your local /etc/hosts file. Add a line pointing your domain to the new server's IP:

# /etc/hosts
203.0.113.50  yoursite.com  www.yoursite.com

Now when you open yoursite.com in your browser, you'll hit the new server while every other visitor in the world still sees the old one. Click through every critical page: homepage, a few posts, the admin dashboard, contact forms, WooCommerce checkout if applicable. Fix anything that breaks.

For headless testing without modifying your hosts file, use curl's --resolve flag:

curl --resolve yoursite.com:443:203.0.113.50 https://yoursite.com

Final Delta Sync

Once testing passes, put the old site into maintenance mode briefly and run one final rsync and database sync to capture any content changes since the initial transfer. Because rsync only transfers changed files, this delta sync typically takes under a minute.

# Enable maintenance mode on old server
wp maintenance-mode activate

# Final file sync (only changed files transfer)
rsync -avz user@old-server:/var/www/html/ /var/www/html/

# Final database sync
# Export from old, import to new, re-run search-replace

DNS Cutover

Update your DNS A record to the new server's IP. With a 300-second TTL, global propagation happens within 5 minutes. Remove your hosts file entry and verify the site loads from the new server. Keep the old server running for at least 48 hours as a fallback.

Post-Migration Checklist

Migration isn't done when DNS switches over. Run through these within the first hour. For a deeper look at optimization after you've moved, check out our WordPress performance optimization guide.

  • SSL certificate: Install and verify. If you're using Let's Encrypt, run certbot --nginx or certbot --apache.
  • Permalinks: Go to Settings > Permalinks and click Save (or run wp rewrite flush). This regenerates .htaccess or Nginx rewrite rules.
  • Caching: Clear all caches. If you use Redis or Memcached, flush them (redis-cli FLUSHALL). Rebuild page cache.
  • CDN origin: If you use Cloudflare or another CDN, update the origin IP to the new server.
  • Email: Verify MX records. If your old host handled email, those MX records may need updating separately from the A record.
  • Google Search Console: Submit your sitemap at the new URL. If you changed domains, use the Change of Address tool.
  • Analytics: Confirm Google Analytics and any other tracking scripts still fire correctly.
  • WP-Cron: Verify scheduled tasks are running (wp cron event list). Some managed hosts use server-side cron instead of WP-Cron.
  • Core integrity: Run wp core verify-checksums to confirm no files were corrupted during transfer.
  • Security cleanup: Delete any migration files left behind (database dumps, migration plugin packages). Rotate salts with wp config shuffle-salts. These are often overlooked and leave sensitive data sitting in your webroot.

If you're seeing bot traffic spiking after the move (common when a site changes IP), our guide to blocking bot traffic covers the layered approach.

Where to Migrate: Hosting That Makes It Easier

If you're migrating because your current host is slow, unreliable, or limiting, here are two managed options built for developers.

Kinsta runs on Google Cloud C2 instances with built-in Redis object caching, automatic daily backups, and free unlimited migrations. Their dashboard gives you SSH access, WP-CLI, and staging environments out of the box. Plans start at $35/month. We covered everything in our full Kinsta review.

Cloudways lets you choose between DigitalOcean, Vultr, AWS, Google Cloud, and Linode as your infrastructure provider. You get a free migrator plugin, dedicated vCPU options, and full server-level control without managing the OS yourself. Plans start at $14/month. Read our Cloudways review for the full breakdown.

Both handle SSL, automatic backups, and server-level caching so you can focus on the site itself after migration.

Troubleshooting Common Migration Issues

White Screen of Death

Usually a PHP memory issue or fatal error. Check wp-content/debug.log (enable WP_DEBUG_LOG in wp-config.php first). Common cause: PHP version mismatch between old and new servers. If the old server ran PHP 7.4 and the new one runs 8.2, deprecated functions may throw fatal errors.

404 Errors on Every Page Except Homepage

Permalinks need flushing. Run wp rewrite flush or visit Settings > Permalinks and click Save. If you're on Nginx, make sure the try_files directive is configured correctly (Nginx doesn't use .htaccess).

Mixed Content Warnings (HTTP/HTTPS)

Some URLs in the database still reference http:// instead of https://. Run wp search-replace 'http://yoursite.com' 'https://yoursite.com' --all-tables to catch any that slipped through.

Broken Widgets and Theme Settings

Classic serialized data corruption. If you used raw SQL REPLACE instead of WP-CLI for the URL swap, the serialized arrays are broken. Restore the database from backup and re-run the migration using wp search-replace.

Frequently Asked Questions

How long does a WordPress migration take?

Depends on site size. A small blog (under 500MB) can be migrated in under 30 minutes. A large WooCommerce site with 10GB+ of media can take several hours for the initial file transfer, but the actual downtime window (final delta sync + DNS cutover) should be under 10 minutes with the two-sync strategy.

Will I lose my SEO rankings if I migrate hosts?

Not if you keep the same domain. Google cares about URLs, not IP addresses. As long as your URLs don't change, your rankings stay intact. If you're changing domains, set up 301 redirects from every old URL to the corresponding new one and use Google Search Console's Change of Address tool.

Can I migrate WordPress without SSH access?

Yes, but your options narrow. Use a migration plugin (Duplicator, All-in-One WP Migration) or your host's built-in migration tool. Many managed hosts like Kinsta and Cloudways offer free migrations handled by their support team.

What about email? Will migration break it?

Only if your old host also handled your email (MX records pointed to the old server). If you use a third-party email service like Google Workspace, Zoho, or Fastmail, your MX records are separate and unaffected. If your old host managed email, update MX records at the same time as DNS and consider switching to a dedicated email provider.

What if something goes wrong? Is there a rollback plan?

Yes. The old server stays fully intact during the entire migration. If something breaks on the new server, revert DNS back to the old server's IP. With a 300-second TTL, traffic returns to the old server within 5 minutes. You can then troubleshoot the new server without any visitor impact.

WordPress migration doesn't have to be stressful. Lower your TTL early, use rsync and WP-CLI instead of plugins, test via the hosts file before cutting DNS, and keep the old server running as your safety net. The developer approach takes a bit more setup, but it works reliably at any scale and keeps your site live through the entire process.