How to migrate from cPanel to a VPS without downtime
Leaving shared hosting is mostly a planning problem, not a technical one. This guide walks through the inventory, the DNS TTL trick, moving files and databases, the email part everyone underestimates, and a cutover checklist that keeps your sites online the whole time.
Most people stay on shared hosting long after they've outgrown it, and it's rarely because a VPS is too expensive. It's because moving feels risky. A dozen sites, a handful of databases, mailboxes that clients use every day, DNS spread across two registrars — and the nagging fear that something will be offline for a day while you figure it out. The good news: a migration done in the right order has no visible downtime at all. The trick is that almost all of the work happens before you change a single DNS record. Here's the playbook.
Step 0: Take inventory before you touch anything
Every painful migration story starts with "we forgot about…". Before you copy a byte, write down what actually lives on the old account:
- Domains: primary, addon and parked domains, subdomains, and where each one's DNS is hosted (the registrar, the shared host, Cloudflare…).
- Databases: every MySQL database and which site uses it. Old cPanel accounts collect orphaned databases from abandoned installs; this is the moment to drop them.
- PHP version and extensions per site. A site that has quietly run on PHP 7.4 for years will not necessarily survive 8.3 on day one.
- Cron jobs, which are invisible until a nightly export stops arriving.
- Email: mailboxes, forwarders, autoresponders, catch-alls — and roughly how much mail is stored.
.htaccessrules: redirects, password-protected folders, rewrite rules. If your new server runs Nginx, none of these are read. They have to be translated.- SSL: which domains have certificates, and whether any are paid certificates you need to carry over.
This list becomes your test plan at the end.
Step 1: Lower your DNS TTL a day or two ahead
DNS records carry a TTL (time to live): how long resolvers may cache the answer. Shared hosts often use 14400 seconds (4 hours) or even 86400 (a full day). If you switch the A record while the TTL is a day, some visitors keep hitting the old server for up to a day.
So 24 to 48 hours before the move, lower the TTL on the A, AAAA and MX records to 300 seconds. Then wait for the old TTL to expire. Check what the world currently sees:
dig +noall +answer example.com A
dig +noall +answer example.com MX
The second column is the remaining TTL. Once it shows 300, a cutover propagates in about five minutes, and so does a rollback if you need one.
Step 2: Get a complete backup archive
On cPanel, use Backup → Download a Full Account Backup. If you have root on the old server, /scripts/pkgacct username produces the same archive faster. On Plesk, use the Backup Manager. The archive contains the home directory (with public_html), every database as an SQL dump, mailboxes and DNS zone data.
Pull it straight to the new server instead of through your laptop — cPanel can push the backup to a remote server over SCP, or you can download it with wget from the new machine. Multi-gigabyte archives over home broadband are where migrations stall.
If you don't trust the full archive (very large accounts sometimes time out), copy files and databases separately:
# on the new server: copy the web root
rsync -avz --progress olduser@old-host:public_html/ /var/www/example/public_html/
# on the old host: dump one database
mysqldump --single-transaction --routines --triggers olduser_wp > olduser_wp.sql
--single-transaction gives you a consistent InnoDB snapshot without locking the live site.
Step 3: Rebuild the site on the VPS
On the new server, create the site — its own Linux user, web root, PHP-FPM pool and vhost — then import files and data:
mysql -e "CREATE DATABASE example_wp; CREATE USER 'example_wp'@'localhost' IDENTIFIED BY 'a-long-random-password'; GRANT ALL ON example_wp.* TO 'example_wp'@'localhost';"
mysql example_wp < olduser_wp.sql
cPanel prefixes database names with the account name (olduser_wp). On the new server you will almost certainly use different names, users and passwords, so update the application's config — wp-config.php, Laravel's .env, and so on. Then fix ownership so the site's user owns its files, and match the PHP version the site ran on before. Upgrade PHP later, as a separate, deliberate step.
Step 4: Test the new server before DNS knows it exists
You can browse the new server under the real domain without changing public DNS. Point the domain at the new IP in your local hosts file, or test from the command line:
curl -I --resolve example.com:80:203.0.113.10 http://example.com/
Click through the site, log in to the admin area, submit a form, check the error log. This is where you find the missing PHP extension, the hardcoded absolute path to /home/olduser/, and the redirect that only existed in .htaccess.
Step 5: Email, the part everyone underestimates
Websites are stateless enough to copy twice; email is not. New mail keeps arriving during the move, and users notice immediately when something is missing. You have three options:
- Leave email where it is for now and move only the websites. Keep the MX records pointing at the old host. Perfectly valid, and it splits one risky move into two easy ones.
- Move email to a dedicated mail provider.
- Host email on your VPS. Create the mailboxes on the new server, then copy existing messages over IMAP with
imapsync:
imapsync --host1 old-host.example --user1 info@example.com --password1 'old' \
--host2 new-host.example --user2 info@example.com --password2 'new'
Run it once before cutover and again after the MX switch to pick up stragglers. Before you move mail to your own server, read up on SPF, DKIM, DMARC and reverse DNS — a new IP with no reputation is exactly what spam filters distrust.
Step 6: The cutover
With a 300-second TTL and a tested server, the switch itself is undramatic:
- Freeze changes on dynamic sites (no new posts, orders or plugin updates for the next hour).
- Run a final delta sync:
rsyncagain (only changed files transfer) and re-dump and re-import the databases. - Switch the A and AAAA records to the new IP. Switch MX only if you move email now.
- Issue SSL certificates as soon as DNS resolves to the new server — Let's Encrypt's HTTP challenge needs the domain to point at the machine requesting the certificate.
- Watch the old server's access log. When traffic there dries up, propagation is done.
- Keep the old hosting for at least a week. It is your rollback and your archive of anything you forgot.
Traps that cause the "it was down all weekend" stories
- Switching DNS with a 24-hour TTL. You can't undo a long TTL after the fact. Lower it first.
.htaccesson Nginx. Redirects and protected folders silently stop working. Translate them into the server config.- Hardcoded paths and URLs. Search the code and the database for
/home/olduserand forhttp://versions of the domain. - Forgotten cron jobs. Re-create them, using the right PHP binary for the site's version.
- Dropping the old account too early. Cancel it only when the new server has served everything — web and mail — for a week.
- Changing everything at once. New server, new PHP version, new mail setup and a theme update on the same evening means you won't know which change broke things. Move first, modernise afterwards.
How ShadowPanel automates the move
ShadowPanel is a self-hosted hosting control panel, and moving off cPanel or Plesk is a built-in workflow rather than an afternoon of shell commands.
- Import a cPanel or Plesk backup archive. Drop the archive into the panel's migrations folder over SFTP (the right choice for large backups) or upload smaller ones in the browser. The panel inspects it read-only, detects whether it came from cPanel or Plesk, and lists the domains, databases and mailboxes it found.
- Pick what to bring over. Choose the target domain and PHP version, whether to import the files, which databases and which mailboxes. The migration then runs as a background job: it creates the site with its own isolated Linux user, PHP-FPM pool and Nginx vhost, copies the web root into place, and imports each database under a fresh managed name, user and password. The result maps every original database to its new name, so updating the app's config is a two-minute job.
- Mailboxes are created with new passwords, shown once at the end so you can hand them out — password hashes from the old host can't be reused. Stored messages aren't copied, so
imapsyncfrom step 5 still applies. - Already copied a site by hand? The Import existing site feature adopts a site that is already on the server's disk. It previews the size, file count, detected app and PHP version, and guesses the database from
wp-config.phpor.env. It then copies the files into the managed layout — never moves them, so the original stays intact — backs up any existing vhost, and adopts or issues the SSL certificate. - The
.htaccessgap is covered by per-site redirects and password-protected paths in the panel, and certificates are issued and renewed automatically (see free SSL with Let's Encrypt).
Both import tools are admin-only, since they operate on the whole server. If you haven't picked a server yet, installing a hosting panel on a Hetzner VPS covers the setup end to end.
The bottom line
A zero-downtime migration is mostly sequencing: take inventory, lower the TTL early, rebuild and test on the new server while the old one keeps serving, handle email as its own project, then switch DNS with a short TTL and the old host as a safety net. None of the steps are hard. Skipping one is what hurts. ShadowPanel takes a cPanel or Plesk archive and turns the rebuild — site, isolation, files, databases, mailboxes — into a guided import, so you can spend your attention on testing instead of plumbing.
curl -fsSL https://shadowpanel.de/install.sh | bash
Related articles
How to set up a staging site for WordPress and Laravel
Testing changes on the live site works until the day it does not. A proper staging environment is a copy you can break s...
How to manage many WordPress sites: updates, backups, security
Ten WordPress sites are a hobby; fifty are a job. Here is a repeatable routine for keeping a whole fleet updated and sec...
SPF, DKIM and DMARC: stop your server's email going to spam
Your contact form works, but the messages land in spam, or never arrive at all. Mailbox providers judge mail on proof of...