Slot 42 Shared hosting method

cPanel to cPanel Migration Without WHM Access

Moving a cPanel account between shared hosts with no root and no Transfer Tool: Backup Wizard, rsync, mysqldump, imapsync, DNS records and a clean cutover.

You are on shared hosting. You have a cPanel login and nothing else: no WHM, no root, no SSH key on the source box. That rules out the tool everyone reaches for first, because WHM’s Transfer Tool needs root SSH on the source server. You will never have it, and asking support will not change that.

What you do have is enough. I move accounts like this regularly, and the whole job comes down to four payloads (files, databases, mailboxes, DNS) plus a cutover that does not lose mail.

Route one: generate a full backup and hand it to the destination host

cPanel’s own Backup or Backup Wizard page (under Files) produces two different things, and the difference decides how the rest of the move goes.

Full account backup Partial backups
Contains Home directory, all databases, email accounts and their mail, forwarders, filters, DNS zone, cron jobs, FTP accounts, subdomains and addon domains Home directory or one MySQL database or email forwarders & filters
Where it lands /home/<user>/backup-<M.D.YYYY>_<HH-MM-SS>_<user>.tar.gz, or a remote FTP/SCP target you specify Same, one file per selection
Who can restore it WHM / root, normally the destination host’s support You, from the cPanel Backup page

That last row is the whole strategy. A full cPanel backup is a cpmove-shaped archive and restoring it properly is a root operation, so the practical move is to generate it, get it onto the destination server, and open a ticket asking them to restore it into your new account. Most shared hosts do this free as part of onboarding. Ask before you start.

To move the archive without downloading 40 GB to your laptop, use the Backup page’s remote destination. Choose Remote FTP Server (or SCP) and give it the destination host’s FTP credentials. A cPanel account’s FTP root is its home directory, which is where the destination host’s support will look for the file.

Partial backups are the underrated half. A home-directory backup and a per-database backup can both be restored by you, from cPanel, with no ticket. If the destination host refuses to restore full backups, this is still the fastest path for files and data.

Route two: fully manual, piece by piece

When nobody will restore anything for you, move each payload yourself.

Files

If the source host allows SSH (many shared hosts do, often on a non-standard port), rsync from the destination server pulling from the source. Run this on the destination:

rsync -avz --progress --delete \
  -e "ssh -p 2222" \
  olduser@old-host.example.net:/home/olduser/public_html/ \
  /home/newuser/public_html/

Exclude cache directories on the first pass so you are not moving junk:

rsync -avz -e "ssh -p 2222" \
  --exclude 'wp-content/cache/' \
  --exclude '.git/' \
  olduser@old-host.example.net:/home/olduser/ /home/newuser/

No SSH on either side? Two fallbacks that work from a plain cPanel:

  1. Build the archive on the source with the Backup page’s home-directory partial backup, then restore it on the destination with the same page’s Restore a Home Directory Backup control. Neither end needs root.
  2. Use the source cPanel’s remote-FTP backup destination pointed at the destination account, so the file never touches your connection.

If you have shell on both ends but rsync is unavailable, tar and copy. Never leave an archive inside public_html where the whole internet can fetch it.

tar -czf ~/site-files.tar.gz -C /home/olduser public_html
scp -P 2222 ~/site-files.tar.gz newuser@new-host.example.net:/home/newuser/

Databases

Over SSH, mysqldump per database. cPanel users cannot dump --all-databases, so list them from cPanel > MySQL Databases and loop:

mysqldump --single-transaction --quick --routines --triggers \
  --default-character-set=utf8mb4 \
  -u olduser_wpuser -p olduser_wp | gzip > ~/olduser_wp.sql.gz

Import on the destination, into the database you created there first:

gunzip < ~/olduser_wp.sql.gz | mysql -u newuser_wpuser -p newuser_wp

Without SSH, use phpMyAdmin: Export > Custom, output compressed with gzip, Add DROP TABLE / VIEW checked, and Add CREATE DATABASE unchecked. That last checkbox is the one people leave on, and it makes the dump try to create a database name your new account is not allowed to own.

phpMyAdmin’s import is capped by upload_max_filesize, usually 50 MB or less. For anything bigger, upload the .sql.gz into the home directory and import over SSH with the gunzip | mysql line above, where no upload limit applies.

Do not reach for split -b 20M on the dump itself. It cuts on byte offsets, not on statement boundaries, so the second chunk begins in the middle of an INSERT and none of the pieces import on their own. If something on the path between the two servers forces you to break the file up for transport, put it back together before you import it:

split -b 20M olduser_wp.sql.gz olduser_wp.part-      # transport only
cat olduser_wp.part-* > olduser_wp.sql.gz            # on the destination
gunzip < olduser_wp.sql.gz | mysql -u newuser_wpuser -p newuser_wp

If you have no SSH at all and the file will not fit through phpMyAdmin, the answer is to ask the host to raise the limit for an hour, not to feed phpMyAdmin fragments.

Email

Mailboxes are the payload that quietly ruins migrations, because mail keeps arriving while you work. Do not copy ~/mail around. Sync it with IMAP so you can run the sync repeatedly and only move the delta. The full procedure, including the flags that matter for cPanel-to-cPanel, is in the imapsync runbook.

Create every mailbox on the destination with the same address and a known password before the first sync, or imapsync has nowhere to write.

DNS

Zone records are hand-copied. Open Zone Editor on the source, and read the live zone from the authoritative nameservers so you catch anything added outside cPanel:

for r in A AAAA MX TXT CNAME SRV CAA NS; do
  dig +noall +answer example.com "$r" @ns1.old-host.example.net
done
dig +short default._domainkey.example.com TXT @ns1.old-host.example.net
dig +short _dmarc.example.com TXT @ns1.old-host.example.net

The records people forget: DKIM (default._domainkey), DMARC, SPF on subdomains, autodiscover/autoconfig CNAMEs, and any TXT record a third party planted for domain verification.

Fixing what breaks after the restore

Files you upload as the cPanel user are owned correctly. Files restored by the host sometimes are not, and you cannot chown as a normal user. If PHP throws permission errors after a restore, ownership is the ticket to open:

# for whoever has root on the destination
chown -R newuser:newuser /home/newuser

Permissions you can fix yourself. Look before you rewrite, and fix the thing you found rather than normalising the tree: chmod 755 on every directory reopens the 0700 ones somebody locked deliberately, and chmod 644 on every file republishes a 0660 config to everyone with a shell on the box. That trades a permissions problem for a disclosure one.

What is actually worth correcting after a restore is group or world writability, and it matters on directories as much as on files:

# what is wrong, before anything changes
find ~/public_html \( -type d -o -type f \) -perm /go=w -printf '%m %p\n' | head -50

chmod go-w removes those two bits and leaves everything else exactly as the restore wrote it. Owner bits are preserved, the executable bit stays on CGI and shell scripts, and a 0700 directory has no group or world write bit to remove, so it is never in the list:

find ~/public_html \( -type d -o -type f \) -perm /go=w -exec chmod go-w {} +

Then take the secrets down to owner-only, which is where they should have been on the old server too. This is the one case where a fixed mode is right, because nobody but the owner has any business reading them:

chmod 600 ~/public_html/wp-config.php
find ~ -maxdepth 4 -type f \( -name '.env' -o -name 'configuration.php' \) \
  -exec chmod 600 {} +

cPanel prefixes the database name and its user with the account username, so olduser_wp becomes newuser_wp and every config file still points at the old one:

// ~/public_html/wp-config.php
define( 'DB_NAME', 'newuser_wp' );
define( 'DB_USER', 'newuser_wpuser' );
define( 'DB_PASSWORD', 'the-new-password' );

Laravel and most modern apps keep the same three values in .env as DB_DATABASE, DB_USERNAME, DB_PASSWORD. This same rename trap is what bites people on cross-panel moves too, since DirectAdmin’s cPanel importer rewrites database names to its own prefix.

Set the PHP version in MultiPHP Manager to match the old host. If the old server ran suPHP or DSO, the .htaccess may contain php_value lines that crash Apache under PHP-FPM with a 500. Move them to MultiPHP INI Editor or a .user.ini file and delete them from .htaccess.

A full account backup carries crontabs. A home-directory-only backup does not. If you took the manual route, screenshot the source cPanel’s Cron Jobs page and retype every line.

Certificates do not travel usefully. Once DNS points at the new server, cPanel’s AutoSSL issues a fresh certificate on its next run, or you can force it from SSL/TLS Status > Run AutoSSL. Paid certificates get reinstalled from the original key and CRT under SSL/TLS > Install and Manage SSL.

The cutover, in order

  1. Three days out, lower the TTL on every record in the live zone to 300 seconds. This is the step that costs nothing and saves the whole cutover.
  2. Two days out, move files and databases. Create all mailboxes on the destination and run the first imapsync pass.
  3. Test before you switch anything. Point your own machine at the new server with a hosts entry and click through the site, including checkout and any form that sends mail:
    # /etc/hosts
    203.0.113.10  example.com www.example.com
  4. At cutover, put the source site into maintenance mode or stop writes. Run a final rsync, a final mysqldump/import, and a final imapsync pass.
  5. Switch DNS. Change the A record first, then MX. With a 300-second TTL the world follows within minutes.
  6. Leave the old account running for seven days. Mail will still land there from resolvers holding stale records. Run imapsync once a day for that week to sweep it forward.
  7. Raise the TTL back to 3600 or higher once you are settled.

Verification checklist

  • Site loads over HTTPS on the new IP with no certificate warning
  • dig +short example.com returns the new IP from at least two public resolvers
  • Admin login works and a database write succeeds (publish a draft, place a test order)
  • Send mail out from the site’s contact form and confirm it is not rejected
  • Send mail in from an external address and confirm delivery to the new mailbox
  • Every mailbox folder count matches the source (imapsync prints this at the end)
  • Cron jobs exist and have fired at least once
  • Error log is quiet: tail -n 100 ~/logs/example.com.error.log
  • Redirects and .htaccess rules still behave: check a deep URL, not just the homepage

FAQ

Can I restore a full cPanel backup from inside cPanel?

No. The Backup page restores home directories, individual MySQL databases, and email forwarders and filters. A full account archive is restored through WHM or the command line, which is why the destination host has to do it. This is the same root requirement that puts the WHM Transfer Tool out of reach on shared hosting.

The destination host says they cannot accept a backup from another provider. Now what?

That usually means they will not restore an archive, not that they will block your files. Take the manual route: home-directory partial backup restored from the cPanel Backup page, databases through phpMyAdmin, mail through imapsync. That works on every cPanel host I have met.

How do I move email if the source host blocks outbound IMAP?

Run imapsync from a third machine that can reach both servers, connecting to each over IMAP on 993. Nothing needs to run on either host. If the source blocks inbound IMAP from outside its own network, ask support for a temporary firewall exception for your IP.

What if the account has addon domains?

Recreate every addon and subdomain on the destination before restoring files, and check that the document root paths match the source exactly. Mismatched document roots are the top cause of “the main site works but the addon shows the main site” after a move.

Where this fits

No-root cPanel moves are slower than a Transfer Tool run, but the failure modes are predictable. If the account is large or the site cannot afford a bad cutover, this is the kind of job I take on as server migration work, including the mailbox sync and the DNS switch.