WHM's Transfer Tool is convenient for small accounts, but when you're migrating a 20GB WordPress multisite, a high-traffic Magento store, or a server with hundreds of cPanel accounts, it often stalls, times out, or crashes halfway through. The all-or-nothing archive approach means one network hiccup restarts the entire transfer from zero. This guide shows you how to migrate any cPanel account manually using rsync for files and mysqldump for databases — a method that's resumable, verifiable, and works even when the Transfer Tool gives up.
Why the cPanel Transfer Tool Fails on Large Accounts
The built-in Transfer Tool works by packaging the entire cPanel account into a .tar.gz archive on the source server, transferring it over HTTP or SSH, then extracting it on the destination. For accounts over ~5-10 GB, this process breaks down in several ways:
- No resume capability — if the transfer is interrupted, the entire process restarts from the beginning
- Double disk space requirement — the tar archive and the original files both need to fit on the source
- Memory spikes during database packaging — large MySQL databases exhaust RAM during the dump phase
- HTTP timeout limits — long-running WHM GUI transfers can be killed by Nginx or Apache proxy timeouts
- Slow compression — compressing millions of tiny cache files (Magento, WordPress object cache) burns CPU for hours
The manual rsync + mysqldump approach avoids all of these: rsync transfers files incrementally, skips unchanged files on reruns, and handles interruptions gracefully.
Pre-Migration Checklist
Before starting, verify these on both servers:
- Disk space: destination needs account size + 20% headroom (
df -h /home) - cPanel version match: within one major version (
/usr/local/cpanel/cpanel -V) - PHP version availability: destination has the same PHP versions installed (
whmapi1 php_get_installed_versions) - MySQL/MariaDB compatibility: source version ≤ destination version (you cannot migrate to a lower DB version)
- Lower TTLs early: set DNS TTL to 300 seconds at least 24 hours before migration so the cutover propagates quickly
Step 1: Set Up Passwordless SSH Between Servers
All rsync commands will run from the destination server, pulling from the source:
# On the DESTINATION server — generate a key if you don't have one
ssh-keygen -t ed25519 -C "migration-key" -f ~/.ssh/migration_key -N ""
# Copy the public key to the SOURCE server
ssh-copy-id -i ~/.ssh/migration_key.pub root@SOURCE_IP
# Test the connection
ssh -i ~/.ssh/migration_key root@SOURCE_IP "hostname"
Step 2: Create the cPanel Account Shell on the Destination
Create the account metadata before syncing files so ownership is correct from the start:
# On DESTINATION — create the account (replace USERNAME, DOMAIN, PASSWORD, EMAIL)
whmapi1 createacct username=USERNAME domain=DOMAIN password=PASSWORD contactemail=EMAIL plan=default
This creates the /home/USERNAME/ directory tree, the cPanel databases, and the system user. You'll overwrite the files in the next step.
Step 3: Transfer Files with rsync
Run rsync from the destination, pulling from the source. Use --exclude to skip cache directories that would waste transfer time:
# Initial sync — this may take a while for large accounts
rsync -avzP --delete --exclude="public_html/wp-content/cache/" --exclude="public_html/var/cache/" --exclude="public_html/var/session/" --exclude="tmp/" -e "ssh -i ~/.ssh/migration_key" root@SOURCE_IP:/home/USERNAME/ /home/USERNAME/
# Fix ownership after sync
chown -R USERNAME:USERNAME /home/USERNAME/
chmod 750 /home/USERNAME/
Rsync is safe to re-run. If the transfer is interrupted, run the same command again — it picks up where it left off, skipping unchanged files.
Step 4: Migrate Databases with mysqldump
Export all databases belonging to the account from the source server:
# On SOURCE server — list all databases for the user
mysql -e "SELECT Db FROM mysql.db WHERE User LIKE 'USERNAME\_%' OR User = 'USERNAME';"
# Dump each database (replace DBNAME with each database found above)
mysqldump --single-transaction --quick --routines --triggers DBNAME > /tmp/DBNAME.sql
# Transfer the dump to destination
rsync -avzP -e "ssh -i ~/.ssh/migration_key" root@SOURCE_IP:/tmp/USERNAME_*.sql /tmp/
Import on the destination:
# On DESTINATION — create the database and user if needed
mysql -e "CREATE DATABASE IF NOT EXISTS DBNAME;"
mysql -e "GRANT ALL ON DBNAME.* TO 'DB_USERNAME'@'localhost' IDENTIFIED BY 'PASSWORD';"
mysql -e "FLUSH PRIVILEGES;"
# Import the dump
mysql DBNAME < /tmp/DBNAME.sql
For very large databases (>5 GB), use pv to monitor progress:
pv /tmp/DBNAME.sql | mysql DBNAME
Step 5: Sync Mail and Configuration Files
Email and cPanel configuration live outside public_html and need separate syncing:
# Mail directory
rsync -avzP -e "ssh -i ~/.ssh/migration_key" root@SOURCE_IP:/home/USERNAME/mail/ /home/USERNAME/mail/
# cPanel user data (email accounts, forwarders, filters, etc.)
rsync -avzP -e "ssh -i ~/.ssh/migration_key" root@SOURCE_IP:/var/cpanel/users/USERNAME /var/cpanel/users/USERNAME
# DNS zone file (if DNS is hosted on this server)
rsync -avzP -e "ssh -i ~/.ssh/migration_key" root@SOURCE_IP:/var/named/USERNAME.com.db /var/named/USERNAME.com.db
After syncing, run cPanel's rebuild scripts to register everything correctly:
/scripts/updateuserdomains
/scripts/rebuildhttpdconf
/scripts/restartsrv_httpd
/scripts/restartsrv_mysql
Step 6: Final rsync Before DNS Cutover
Run one last rsync pass immediately before switching DNS to capture any files changed since your initial transfer:
rsync -avzP --delete -e "ssh -i ~/.ssh/migration_key" root@SOURCE_IP:/home/USERNAME/ /home/USERNAME/
# Re-dump the database if there have been writes
mysqldump --single-transaction --quick DBNAME | ssh -i ~/.ssh/migration_key root@SOURCE_IP "mysql DBNAME"
Step 7: DNS Cutover and Verification
Update the A record at the domain registrar or authoritative DNS to point to the destination server IP. Because you pre-lowered the TTL to 300 seconds, propagation happens within 5-10 minutes for most resolvers.
Verify the site loads from the destination before the old server's TTL fully expires:
# Check site via destination IP directly (bypass DNS)
curl -H "Host: DOMAIN.COM" http://DESTINATION_IP/
# Monitor HTTP response
watch -n 5 "curl -s -o /dev/null -w '%{http_code}' https://DOMAIN.COM"
Keep the source server running for at least 48 hours post-cutover in case any DNS resolvers are still serving the old record. For fully managed server migration without the risk of downtime or data loss, CloudHouse Technologies' server migration service handles the entire process for you.
Troubleshooting Common Manual Migration Errors
- rsync: [sender] write error: Broken pipe (32) — add
-e "ssh -o ServerAliveInterval=60"to the rsync command - ERROR 1153 (08S01): Got a packet bigger than 'max_allowed_packet' — add
--max_allowed_packet=512Mto your mysql import command - chown: invalid user after rsync — the system user was not created; run
whmapi1 createacctfirst (Step 2) - Site shows wrong content after DNS cutover — flush Redis/Varnish cache and wait 5 minutes; old cached responses may still be serving
- Email not working after migration — verify MX records point to the destination and run
/scripts/restartsrv_dovecot
