Migrating WHMCS between servers without downtime is doable. Most "migrations" people describe are actually re-installs with data restore, which is fine for small operations but unacceptable for a hosting business with active customers. This is the actual zero-downtime procedure.
I've done dozens of these — small VPSs, multi-tenant cPanel clusters, on-prem to cloud. The principles are the same.
Three types of "migrations" — pick the right one
| Type | When to use | Downtime |
|---|---|---|
| Lift & shift | Same OS, same control panel, new hardware. Most common. | 0–5 min |
| Re-platform | Different OS or panel (cPanel → Plesk). Rebuilds the OS layer. | 15–60 min |
| Re-architect | Different topology (single server → DB-separated, or to cloud-managed services). | 1+ hours, careful planning |
This guide covers lift & shift in detail (the 80% case), with notes on the other two.
Pre-migration checklist
Run all of these before migration day:
- Provision the new server with the exact same OS + PHP + MySQL versions (or higher). Don't take the migration as an opportunity to upgrade — do one change at a time.
- Install WHMCS dependencies on the new server: PHP extensions, ionCube loader, web server, MySQL.
- Test that the new server can reach all the external services WHMCS talks to: payment gateways, registrars, SMTP. Some gateways have IP allowlists.
- Buy a domain or have a hostname ready for the new server. You'll DNS-cut over to it at the end.
- Decide on the maintenance window. Even with zero downtime, you typically pause customer-facing writes for a few minutes during the final sync.
The migration procedure — step by step
Phase 1: Pre-stage on the new server (no downtime)
# On the new server:
# 1. Set up MySQL with a database for WHMCS
mysql -e "CREATE DATABASE whmcs_new;"
mysql -e "CREATE USER 'whmcs'@'localhost' IDENTIFIED BY 'strong-password';"
mysql -e "GRANT ALL ON whmcs_new.* TO 'whmcs'@'localhost'; FLUSH PRIVILEGES;"
# 2. Initial sync of WHMCS files from old to new (rsync)
# Run from the OLD server:
rsync -avz --exclude='.git' /path/to/whmcs/ user@new-server:/path/to/whmcs/
# 3. Initial DB dump and restore
# On OLD server:
mysqldump --single-transaction whmcs_prod | ssh user@new-server "mysql whmcs_new"
# 4. Update configuration.php on NEW server to point to whmcs_new
# On NEW server:
nano /path/to/whmcs/configuration.php
# Change $db_name to 'whmcs_new', $db_username and $db_password to match
# IMPORTANT: Do NOT change the encryption hash ($cc_encryption_hash) — must stay identical
The encryption hash is the most critical detail. WHMCS encrypts server passwords, payment gateway credentials, and saved card tokens using this hash. If it changes, all that data is unrecoverable. Always copy configuration.php from the old server (changing only the DB credentials) — never recreate it.
Phase 2: Test the new server (no downtime)
The new server has data as of the rsync moment. It's stale (customer activity continues on old) but you can test it.
- Add the new server's IP to your hosts file:
X.X.X.X yourwhmcs.com. - Visit your WHMCS in a browser. Login as admin. Click around.
- Confirm: admin login works, services list loads, ticket list loads, no PHP errors.
- Run a test cron:
php /path/to/whmcs/crons/cron.phpon the new server. Watch for errors. - Remove the hosts file entry.
If any of these fail, fix on the new server now, before the final cutover.
Phase 3: The cutover (brief downtime — 2-5 minutes)
Pick a low-traffic window. Communicate planned maintenance to customers if you want — a 5-minute window is usually below the "we need to notify" threshold.
# 1. Enable WHMCS maintenance mode on OLD server
# Setup → General Settings → Other → Maintenance Mode: enabled
# This blocks customer-facing pages but allows admin (and the API) to keep working
# 2. Stop the WHMCS cron on OLD server
# crontab -e on old server, comment out the cron line
# 3. Wait 30 seconds for any in-flight requests to complete
# 4. Final DB sync (this is the moment that captures all data created since Phase 1)
# On OLD server:
mysqldump --single-transaction whmcs_prod | ssh user@new-server "mysql whmcs_new"
# 5. Final file sync (catches any uploaded attachments)
rsync -avz --delete /path/to/whmcs/attachments/ user@new-server:/path/to/whmcs/attachments/
rsync -avz /path/to/whmcs/downloads/ user@new-server:/path/to/whmcs/downloads/
rsync -avz /path/to/whmcs/templates_c/ user@new-server:/path/to/whmcs/templates_c/
# Note: templates_c is the Smarty compiled cache; safe to clear if you prefer
# 6. Enable cron on NEW server
# crontab -e on new server, add the cron line pointing to /path/to/whmcs/crons/cron.php
# 7. Switch DNS
# Update A/AAAA records for yourwhmcs.com to point to the new server's IP
# TTL should already be low (60-300 seconds) from earlier prep — if not, you'll see lingering traffic on the old server
# 8. Disable maintenance mode on the NEW server (it's now serving traffic)
Customers see at most a "We're doing maintenance" page for a couple of minutes. Anyone with cached DNS may still hit the old server for the TTL duration, but the old server is in maintenance mode so they see the maintenance page (not stale-write opportunity).
Phase 4: Post-cutover verification (still during the window)
From an external browser (not the new server, not behind your office NAT):
- Visit your WHMCS — it should load from the new server.
- Log in as admin. Verify all data is current (check the latest order's timestamp).
- Run a force-cron:
php /path/to/whmcs/crons/cron.phpon the new server. Watch for errors. - Send a test email from WHMCS (Utilities → Send Test).
- Place a test order through the customer-facing flow.
If anything's broken: don't roll back yet. The old server is still in maintenance mode, so reverting is "swap DNS back" + "disable maintenance on old, enable on new." But usually the issue is small — port not open, file permission, etc.
Phase 5: Stand down the old server (days later)
Wait 7 days before decommissioning the old server. Reasons:
- DNS cache: some ISPs ignore TTLs. Traffic can leak to the old server for days.
- Backup target: if anything goes wrong on the new server, you can promote the old server back.
- Audit: you'll discover small issues (a cron task you forgot, an external service still pointing at old IP) over the first week.
After 7 days of clean operation on the new server, snapshot the old server, then power it off.
Notes for re-platform migrations
If you're going from cPanel to Plesk, Linux to Linux (different distro), or PHP 7.4 to PHP 8.x at the same time:
- Do the WHMCS migration first, on the same OS/PHP. Lift & shift only.
- Then upgrade in place on the new server. Smaller, isolated changes.
- Resist the urge to do everything at once. One change at a time means you know what broke.
Notes for cloud-managed migrations
If you're moving to managed services (e.g., AWS RDS for the database, separate from the WHMCS app server):
- Test the latency between the WHMCS app server and the managed DB before committing. WHMCS makes many DB queries per page; high latency makes it feel slow.
- Use VPC private networking — don't expose RDS to the public internet.
- Set up automated DB backups via the cloud provider's snapshot feature. Disable WHMCS-side DB backups if they duplicate the cloud snapshots.
How to verify migration succeeded
- Total record counts match between old and new DBs for critical tables:
SELECT COUNT(*) FROM tblclients, tblhosting, tblinvoices, tbltickets. - Latest record dates match (or are slightly newer on new) for each table.
- License is valid on new server (Utilities → System → System Information).
- Cron runs cleanly on new server, fails on old server (because it's stopped).
- End-to-end customer flow works: signup → pay → provision → email → login → ticket.
Common pitfalls
"Server passwords don't work after migration." The encryption hash in configuration.php changed. Restore the original. If you've already lost it, you'll have to reset every server password manually.
"Customers complain about emails not arriving." SMTP credentials moved with WHMCS, but the outbound IP changed. Either:
- Your SMTP provider whitelists IPs — add the new server's IP.
- You're using local PHP mail — set up SPF/DKIM/DMARC for the new server's IP.
"Payments fail with gateway timeout after migration." Gateway whitelists are IP-based. Update them with the new server's outbound IP. Most gateways have a 5-minute portal change.
"DNS hasn't propagated and old server is still serving stale data." You forgot maintenance mode on the old server, or you set a long TTL pre-migration. Set TTL to 60-300 seconds at least 48 hours before migration so changes propagate fast.
"Module Log on new server is empty." The log table didn't migrate, or migrated but file system permissions are wrong on storage/logs/. Check ownership: should be the web user (www-data, nginx, etc.).
My take — what to do before, never during
- Pre-stage everything possible. File sync, DB dump, configuration verification all happen days before the cutover window. Migration day is just the final sync + DNS switch.
- Don't change anything else. Migrations + WHMCS upgrades + PHP version bumps all at once = chaos. One change at a time.
- Have a rollback plan in writing. "If X fails, run these commands to revert." Don't improvise rollback under pressure.
- Keep the old server alive for 7 days. Cheap insurance.
Going further
- Official WHMCS migration docs
- System requirements guide — what to provision on the new server.
I migrate WHMCS installs between servers and clouds without downtime — pre-staging, the rsync/dump sequence, the cutover, the verification. If you're staring at this task and dreading it, tell me your situation and I'll send a quote + plan in 24 hours.