cPanel migration: move accounts from cPanel to Jabali Panel

Last updated

The cPanel ingest path. Status: production-supported.

Source archive

The standard cpmove-<user>.tar.gz produced by cPanel’s Backup Wizard or pkgacct. Either format works.

For server-wide migration, produce a separate cpmove per account or feed a WHM-level dump through the WHM pipeline.

What gets migrated

AssetBehavior
Linux user accountRecreated under the destination panel; UID may differ.
Home directory contentsCopied to /home/<user>/.
Hosted domainsCreated as panel Domain rows; vhosts rendered by the reconciler.
SubdomainsCreated as Domain rows (or aliases, per heuristic on the cPanel addon_domains map).
DNS zonesTranslated to PowerDNS schema rows.
MySQL databasesRestored via mysql against panel-managed MariaDB.
MySQL users + bcrypt password hashesPreserved: the panel stores the bcrypt hash directly so migrated apps keep authenticating without password reset. See commit be788a87.
Email accountsCreated in Stalwart. Cleartext passwords are not preserved (cPanel uses Dovecot/CRAM-MD5 which Stalwart’s Argon2id store rejects); generated passwords are printed in the migration report for out-of-band delivery.
DKIM keysImported as-is so mail flow is uninterrupted.
Mailing listsNot migrated, Mailman is not currently in the panel; the affected accounts surface in the migration report.
FTP accountsMapped to SFTP via Match Group + the user’s SSH key vault. cPanel FTP-only passwords do not transfer (FTP is plaintext; the panel does not host FTP).
Cron jobsTranslated into systemd-user timers if the command passes the Cron allowlist. Disallowed commands surface in the report; the operator may add them to the allowlist and re-run.
SSL certificatesRe-issued by the reconciler via Let’s Encrypt; the cPanel cert is not migrated (paths and renewal hooks differ).

What is not migrated

  • WHMCS / Reseller-only configuration (Jabali has no reseller construct).
  • cPanel “Backup Manager” snapshots (use the new panel’s Backups going forward).
  • Spamassassin per-mailbox rules (Stalwart handles spam scoring server-side).
  • Apache .htaccess files referencing modules nginx does not have (mod_rewrite is supported via the nginx vhost template; mod_php directives are dropped).

Commands cheat sheet

Every command below is run on the source cPanel/WHM host unless noted:

# Per-account archive (Backup Wizard equivalent, headless)
/scripts/pkgacct <username>
#   -> /home/cpmove-<username>.tar.gz

# Whole-server dump (one cpmove per account)
/scripts/cpbackup

# Copy archive to Jabali Panel host with resume
rsync --partial --progress cpmove-<username>.tar.gz \
    root@<jabali-host>:/var/lib/jabali/migrations/incoming/

On the Jabali Panel host:

# List queued migrations
jabali migration list

# Analyze without restoring
jabali migration analyze <archive>

# Restore
jabali migration restore <archive>

# Retry a failed per-account restore from a WHM dump
jabali migration retry <job-id>

# Prune domains left over from soft-deleted cPanel accounts
jabali domain orphan-prune --dry-run
jabali domain orphan-prune --apply

Cutover playbook (minimise DNS-propagation downtime)

The general shape of a low-downtime cutover:

  1. T-48h — Lower TTLs. At the DNS provider (Cloudflare, Route 53, Namecheap, whichever hosts the zone), drop the record TTLs on the domains you’re moving to 300 seconds. Wait 24h so resolvers pick up the shorter TTL.
  2. T-1h — Freeze writes on the source. Put the source cPanel account into maintenance (or ask the tenant to stop posting/uploading). Prevents split-brain between old and new hosts.
  3. T-1h — Generate the cpmove. pkgacct <user> on the source host.
  4. T-45min — Ship the archive. rsync --partial to the Jabali Panel host.
  5. T-30min — Restore. Analyze, then Restore. Watch the per-phase log.
  6. T-15min — Smoke-test on the new host. Hit the new server IP directly with a Host: header override:
    curl -sI --resolve example.com:443:<new-ip> https://example.com/ | head
    Load WordPress admin, log a mailbox in via Roundcube, run mysql -u <migrated-user> -p<pw> -e "SELECT 1;".
  7. T-0 — Repoint DNS. Update A/AAAA/MX at the registrar. Given the 300s TTL, most of the world sees the new host within 5-10 minutes.
  8. T+1h — Issue SSL. Once DNS resolves, toggle the per-domain SSL switch. Certbot issues via the panel’s HTTP-01 challenge on port 80.
  9. T+24h — Restore original TTL. Bump TTLs back to 86400 at the DNS provider.

The full end-to-end guide on the blog covers the same playbook with screenshots + WHMCS-billing edge cases.

MySQL bcrypt hash preservation

MySQL user passwords in cPanel are stored as bcrypt hashes in mysql.user.authentication_string. The panel stores the hash directly — no re-hashing, no password reset — so:

  • WordPress wp-config.php connects without any change.
  • Legacy PHP apps whose DB user is hardcoded in a config file keep working.
  • Only the DB host in the app config may need updating (from localhost to the panel’s socket path or 127.0.0.1).

See commit be788a87 for the rationale and the compatibility matrix (MariaDB 10.6 through 11.x).

Migrating multiple cPanel accounts at once

For >1 account, produce one cpmove per user and batch them, or use the WHM pipeline:

# Source: one archive per account
for user in $(ls /var/cpanel/users/); do
  /scripts/pkgacct "$user"
done

# Batch-ship
rsync --partial --progress /home/cpmove-*.tar.gz \
    root@<jabali-host>:/var/lib/jabali/migrations/incoming/

# On Jabali: restore N accounts in parallel
jabali migration restore-all --parallel 4

The --parallel flag caps concurrent restores. Sensible upper bound: min(CPU cores / 2, 8) — MySQL import is the CPU hotspot.

Operator workflow (short version)

  1. Produce the cpmove-<user>.tar.gz on the cPanel host.
  2. Upload to /jabali-admin/migrations (web) or SCP to /var/lib/jabali/migrations/incoming/.
  3. Click Analyze on the row. Review the report.
  4. Click Restore.
  5. After completion, communicate generated mail passwords to mailbox owners (or set a force-first-login password-reset policy under Server Settings so they self-serve).
  6. Update DNS at the registrar to point to the new panel.
  7. Issue SSL via the per-domain SSL toggle.

Troubleshooting

“Analyze” fails with unrecognized cpmove format. The archive was produced by an unsupported cPanel version (< 11.90 has legacy format differences). Regenerate on the source with pkgacct --version ≥ 11.90.

MySQL restore fails: Error: Duplicate entry '<db-name>' for key 'PRIMARY'. The destination already has a database with the same name. Either drop the destination DB first (DROP DATABASE <db-name>;) or rename the incoming DB in the Analyze report before Restore.

Mailboxes restore but IMAP shows “authentication failed”. Expected — cPanel Dovecot/CRAM-MD5 hashes are not portable. The Restore report prints new generated passwords for each mailbox. Distribute them out of band, or set the force-reset flag in Server Settings so mailbox owners self-serve.

Certbot fails after cutover with no valid A record. DNS hasn’t propagated. Run dig example.com A @1.1.1.1 and wait until the answer matches the new IP. Then retry the SSL toggle.

A domain restored but the site shows a “Welcome to nginx” page. The vhost render is queued in the reconciler (60-second interval). Wait one interval, then reload. If it persists, jabali reconciler run --once --domain example.com forces immediate reconciliation.

Audit

Each phase emits an audit row; the per-domain creation also writes one domain.create row per domain.

Frequently asked questions

Can I migrate a cPanel account to Jabali Panel?
Yes. Jabali Panel's Migrations section accepts the standard `cpmove-.tar.gz` archive produced by cPanel's Backup Wizard or `pkgacct`. Both formats work. Upload one archive per account, or feed a WHM-level dump through the WHM pipeline for server-wide migration.
What gets migrated from cPanel to Jabali Panel?
Linux user account (UID may differ), addon and parked domains, DNS zones, mailboxes and forwarders, MySQL databases with users, PHP-FPM site configs, cron jobs, SSH keys, SSL certificates, and website files under public_html. WordPress installs are detected and re-registered with the panel's WordPress tooling. cPanel-specific features without a Jabali equivalent (SpamAssassin scores, custom Apache handlers) are skipped and logged.
Do I need to stop cPanel before migrating?
No. Generate the `cpmove` archive with `pkgacct ` on the cPanel host at a low-traffic time, copy it to the Jabali Panel server, and upload via the Migrations section. Keep the source cPanel account online until DNS is switched. Restore is idempotent — repeat runs update in place.
How long does a cPanel to Jabali migration take?
Typically 2–10 minutes per account for small sites (under 5 GB, one DB, one domain). Large accounts (100 GB+ web files, multiple databases) are bounded by archive extraction and MySQL import; the UI shows live per-step progress. Multiple accounts can be migrated in parallel.
Does Jabali Panel support WHM server-wide migration?
Yes. See the [WHM pipeline](./whm-migration.md) for a full server-wide dump. It runs one cpmove per account under the hood, tracks progress per account, and lets you retry failed accounts individually.