WHM & Cpanel - Migration
Migrating WHM/cPanel from Rocky Linux 9 to Ubuntu 24.04: Lessons from a Production Cutover
Recently I led a production cPanel server migration from Rocky Linux 9.7 to Ubuntu 24.04 LTS. The server hosted many user accounts with email, websites, and acted as the DNS master in a two-node DNS cluster. Here's what I learned along the way.
Why migrate at all?
Rocky Linux 9 was dropped from cPanel's supported OS list starting v134, which only supports AlmaLinux, CloudLinux, and Ubuntu LTS. With no in-place upgrade path from Rocky to anything cPanel-supported, the only option was building a new server and migrating accounts over.
I chose Ubuntu 24.04 over AlmaLinux 9 — a decision worth thinking about carefully. AlmaLinux would have been the safer same-family migration (RHEL → RHEL), but Ubuntu fit our long-term direction.
The strategy: keep the old IP
The plan kept the original public IPs by building Ubuntu on a temporary IP, migrating data ahead of time, then swapping IPs during a short maintenance window. Pre-migrating to a temporary IP allowed almost everything (accounts, mail, DNS zones) to be moved while the old server stayed live. The cutover window only needed to handle delta sync and the IP swap itself.
The maintenance window was scheduled for a low-traffic Sunday slot, with an estimated 2–4 hour downtime. Network team support was arranged in advance to handle the actual NAT IP swap on the firewall.
DNS: the riskiest piece
Because the source server was also the DNS master pushing zones to a secondary nameserver, DNS handling was the most critical part. The cluster used cPanel's "Synchronize Changes" role — the master pushed updates to the slave one-way.
The saving grace during cutover was NS redundancy at the DNS protocol level. When the primary nameserver went offline briefly to swap IPs, resolvers automatically queried the secondary. This worked independently of the cPanel cluster mechanism, as long as the secondary held the latest zones.
Before cutover, I forced a full zone sync to the secondary and verified SOA serials matched on both sides. TTLs on all critical records were lowered to 300 seconds 48 hours in advance.
Transfer Tool gotchas
The WHM Transfer Tool has a default option called Live Transfer that automatically suspends source accounts after copying. For pre-migration runs where I wanted the old server to keep serving users, this had to be disabled in the Transfer Configuration for every account.
Another surprising option was Update DNS Zone. With it enabled, the tool rewrote DNS records to point at the temporary IP. With it disabled — in our version — no zones synced at all. The workaround was to use WHM's separate Synchronize DNS Records feature once the DNS cluster trust was established between the new server and the old one.
The cutover night
The actual switch followed this sequence:
- Force-sync DNS zones from the old server to the secondary nameserver one last time
- Stop services (Exim, Dovecot, Apache) on the old server
- Change DNS role to Standalone to prevent the old server from pushing during transition
- Flush Exim queue — exim -qff, wait for exim -bpc to reach zero. Frozen messages (stuck bounces) were removed with exiqgrep -z -i | xargs exim -Mrm
- Final rsync of mail and home directories
- Network team swapped the public IPs at the NAT/firewall layer
- Re-activated cPanel license with /usr/local/cpanel/cpkeyclt against the new IP
- Fixed permissions and quotas: /scripts/fixmailperm + /scripts/fixquotas (essential for cross-distro mail directories)
- Restored cluster role to Synchronize Changes on the new server
- Verified DNS resolution, email sending (DKIM still valid), and website access
Cross-OS surprises
A few things bit me during cutover:
Account suspension state was inconsistent. Some users had SUSPENDED=1 in /var/cpanel/users/ but didn't show in whmapi1 listsuspended. The fix was either unsuspendacct --retain-service-proxies or manually cleaning the user file and rebuilding cache with /scripts/updateuserdomains.
DNS cluster labels appeared reversed on one side of the cluster after migration. Testing in both directions confirmed the actual flow was correct, but the UI labeling needed adjustment.
PowerDNS with BIND backend on Ubuntu behaved differently from BIND on Rocky for some cluster operations. Single-record syncs and full-zone operations sometimes needed manual pdns_control reload to surface in PowerDNS.
WHM terminal WebSocket broke immediately after IP swap due to browser cache. Solution: open a fresh incognito window with the new IP.
What I'd do differently
Keep the old IP was the right call. SPF, PTR/rDNS, DKIM alignment, and mail reputation all stayed intact — no warm-up period, no spam complications.
Pre-migrate aggressively. Running Transfer Tool days in advance with Live Transfer disabled meant the cutover window only handled delta sync, which finished quickly.
Verify the secondary nameserver before touching the primary. Confirming the secondary held the latest zones and could resolve everything independently turned the IP swap into a low-risk operation.
Don't trust UI for state. After migration, multiple cPanel UI displays didn't match the underlying filesystem state. Always cross-check with CLI: whmapi1 listsuspended, cat /var/cpanel/users/<user>, and ls /var/cpanel/suspended/.
Final thoughts
Cross-distro cPanel migrations are doable but require treating them as a different problem from same-OS upgrades. The Transfer Tool handles most of the work, but edge cases around quotas, suspension state, DNS backend differences, and Live Transfer defaults all need conscious handling.
Total downtime stayed within the planned window. No data loss. No mail in spam afterward. The source server was kept snapshotted for rollback for two weeks before being decommissioned.
Dat Trinh — Senior DevOps/Cloud Engineer