Skip to content

Transform changelog

This is the release history of corepanel-transform, the tool that turns a cPanel server into a CorePanel server in place. It is kept here because the tool is used once per server, and the version you happen to run is the version your migration behaves like — so what changed between builds is operational information, not trivia.

Terminal window
corepanel-transform version

To upgrade, run the installer again — it always fetches the current build, verifies it against its published checksum and replaces the binary:

Terminal window
curl -fsSL https://get.corepanel.net/check | sh -s -- --install

Do this before you start, not in the middle of a run. A run in progress is tied to the plan it began with, and the tool refuses a plan whose step list does not match the binary performing it — which is a guard rail, not an inconvenience.

Version numbers are CorePanel’s own release numbers, and the tool is only rebuilt in the releases that changed it, so the list below has gaps. A version that is not here changed nothing in the transformation.


  • A password hash too costly to check is no longer carried across as if it worked. CorePanel now refuses, before doing any of the work, to verify a stored hash whose cost is far above anything a sign-in should spend: SHA-crypt past 700,000 rounds, bcrypt past cost 12, argon2 past 64 MiB, 4 passes or 16 lanes, and yescrypt past 64 MiB. The check and the transformation now apply the same limits, so a credential like that is counted with the ones that cannot be preserved (unpreservableCreds) and needs a new password after the move, like a hash in an unsupported format. Until this release it was copied as preserved, and from then on every sign-in with it was refused. cPanel’s own defaults are far below these limits, so this only concerns hashes set by hand or by other tools.

    If your server was transformed before this: a user with one of those hashes is refused at every sign-in. Set a new password for that mailbox, FTP account or account in the panel; nothing else on the server needs to change.

  • A reseller’s own WHMCS keeps working, limited to that reseller’s accounts. On cPanel a reseller can sign their own WHMCS in with their own WHM token. Those tokens now act as the reseller of the same name: their WHMCS sees, creates and changes only their own accounts. The check names those resellers (whm.reseller_credentials), and says that WHM’s per-reseller privileges are not applied. Until this release, a reseller’s token was accepted with root’s reach: it could list, change and remove any account on the server. See a reseller’s own WHMCS.

    If your server was transformed before this: run corepanel whmcompat credentials. Any user other than root is a reseller’s credential. corepanel-whmcompat 1.10.4, with corepanel-core 1.10.4, limits it to that reseller’s accounts. Automatic updates install both overnight; to install them now, run dnf update 'corepanel*'. corepanel-whmcompat is not part of the corepanel package, so updating that one is not enough. If you would rather not wait, remove the credential: corepanel whmcompat revoke-credential --user <reseller> --all. To see whether it was used before the update, and for what: grep 'user="<reseller>"' /var/lib/corepanel-whmcompat/logs/whmcompat.log. Every change it made is on a line that starts MUTATING; the lines dated before the update are the ones made with root’s reach.

  • Accounts with a shell keep one, jailed. jailshell and /bin/bash accounts now land at CorePanel’s shell level: a console inside CorePanel’s jail, with the account’s own files and a fixed set of tools. Until this release a jailshell account arrived at sftp and lost its console, and a bash account arrived at legacy, an unjailed shell the panel’s keys did not govern. Password login comes across too, wherever cPanel’s sshd allowed it. The plans imported from cPanel now allow shell; they used to stop at sftp, which on most servers would have capped nearly every account, since nearly every account has a plan. A package you set up lower still caps the level; a bash account under such a package stays at legacy rather than lose its console. The run says how many accounts land at shell. See SSH comes across too.

  • A rollback gives each account its cPanel login shell back. An account at shell has the jail’s /bin/bash as its login shell, and handed back to cPanel it would have kept it, unjailed. The rollback now restores the shell the account had on cPanel.

  • The cutover warns when CageFS is still on. CageFS and CorePanel’s SSH jail cannot share an account: with CageFS on, every SSH and SFTP login is reset. The cutover already switches CageFS off; if it is still on when CorePanel takes SSH over, the run says so.

    If your server was transformed before this: the accounts keep the level they arrived at; corepanel account ssh status <account> shows one’s. To give one a jailed shell, run corepanel account ssh set <account> --level shell. On a legacy account this first imports the keys in its ~/.ssh. Moving to shell ends the account’s password login; to keep it, run corepanel account ssh password allow <account> --no-expiry. The plans imported then stop at sftp; that limits only your resellers. If they should be able to give shells, raise the SSH maximum of those packages under Packages.

  • corepanel-transform room opens a private conversation with our team, tied to the server’s summary, for help before, during or after a transformation. Only when you run it and say yes. See talk to us.

  • The health gate fails when nobody can sign in. With corepanel-auth stopped, mail and FTP still answered and the gate went green, while every mailbox refused its password. It now asks corepanel-auth directly, and fails naming it.

    If your server was transformed before this: check systemctl is-active corepanel-auth.

  • Transformed domains keep their DKIM key. Keys were made before the cutover installed the mail filter, so every one failed: zones went out without DKIM and mail left unsigned. Now cPanel’s own key is carried (selector default), so signatures stay valid even where DNS is hosted elsewhere; a domain cPanel had no key for gets a new one.

    If your server was transformed before this: generate a DKIM key for each sending domain in the panel (the domain’s email authentication).

  • Each domain keeps where its mail was delivered. A domain whose mail cPanel sent to another provider (Google Workspace, Microsoft 365) arrived delivering locally, so mail the server sent to it — contact forms, WordPress — landed in a mailbox nobody reads. It now arrives as With another provider, and a domain the server was backup MX for stays one. The check lists them before PREPARE.

    If your server was transformed before this: compare /etc/remotedomains and /etc/secondarymx (or their .rpmsave) with each domain’s Email delivery.

  • The rollback window no longer reports “impaired” by mistake. The mail and FTP units cPanel’s packages take with them come back with the rollback’s reinstall. Nothing to do.

  • Pipes to scripts come across. A forwarder, a default address or a mail filter that piped to a program — support@ → |/home/user/public_html/support/pipe.php, the way every cPanel ticket system receives mail — kept nothing of its pipe: a forwarder that only piped was not created at all (listed as one that “discards mail”), one that also forwarded to addresses lost the pipe without a word, and a filter’s pipe was dropped as unsupported. CorePanel now runs mail pipes, and the transformation carries them: the script stays in the home where it is, |/usr/bin/php -q …, |/usr/bin/env bash … and $home/… are translated, with the arguments split the way Exim splits them (a quote cPanel left unclosed when it rewrote the line runs to the end, as it does there), and a pipe that cannot be one here (a program outside the home, a shell command, one of cPanel’s own) is named in the report while the forwarder keeps its addresses. A message two filters pipe reaches both scripts, and the same script named by two rules runs once, as on cPanel. See what gets imported.
  • The check names the pipes that will not run afterwards. A forwarder, default address or filter piping to a program outside the account’s home, a shell command, one of cPanel’s own programs, or a script that is missing or cannot be executed runs on cPanel today and would not after the transformation; the check now lists each of them, account by account, before PREPARE, while the script can still be moved into the home. Before, they surfaced only in the import report, after the cutover. See Mail pipes that will not run afterwards.
  • An address whose pipe cannot come across refuses its mail, instead of feeding the catch-all. A forwarder that only piped to such a program used to be left out, and on a domain with a default address the address then stopped existing: its mail went to the catch-all — another script, or somebody’s inbox — and nobody was told. It now refuses its mail with 550 5.1.1 — what a sender got on cPanel whenever that pipe failed — and the forwarder list says why. An address cPanel refused with :fail:, left out the same way before, now refuses its mail too.
  • A mailbox that also forwarded keeps both. An address that is a mailbox and a forwarder delivers to both on cPanel; its forwarder was refused here (it “would divert all of its mail”), so the copy — or the pipe, for a ticket address that also had a mailbox — stopped going out without a word. It now comes across naming the mailbox among its destinations, and the mailbox keeps its mail.
  • An address with an autoresponder no longer shows up as a forwarder that discards mail. cPanel’s autoresponder is itself a pipe in the forwarder file; the report listed each one as dropped. The autoresponder was always imported.
  • Account-level mail filters are named in the report. cPanel keeps them in .cpanel/filter.yaml; the tool read the file next to it, which is the Exim rendering, and reported a parse error instead. They are still not applied — a CorePanel filter belongs to one mailbox — but each one is now listed by name.
  • A mailbox’s filters keep their order. They were imported sorted by name, so a rule that ended with Stop processing rules could end up after the rules it was meant to stop. They now run in the order cPanel ran them.
  • A filter condition that matches is read as the regular expression it is. cPanel’s matches is a regular expression; it was imported as a wildcard pattern, so ^Invoice .* matched only a subject that literally began with ^Invoice. One a wildcard pattern can say (literal text, ., .*, the anchors) is translated. A filter with any condition that cannot be translated is now left out whole and named in the report: before, the condition alone was dropped, and what was left of an and matched more mail than the filter did.
  • An autoresponder’s end date no longer runs a day long. cPanel stores the instant it stops, midnight after its last day; that instant was read as the last day itself, so the autoresponder answered one day more than it did on cPanel.
  • A filter that discarded mail discards it again. cPanel’s Discard Message is a save to /dev/null; it was imported as a move into a folder called null, so the messages it was meant to drop piled up there.

If your server was transformed before this, its piping forwarders are missing (or forward without their pipe) and its filters lost their pipe actions; nothing recreates them on its own. While cPanel’s files are still on the server, /etc/valiases/<domain> lists each forwarder with its | command, and each mailbox’s filters are in ~/etc/<domain>/<mailbox>/filter.yaml. Recreate a forwarder with corepanel mail forwarder set support@example.com --pipe public_html/support/pipe.php (add its addresses before --pipe if it had any), a default address with corepanel mail catchall set example.com --pipe …, and a filter’s pipe in the mailbox’s Filters. The same files show which filters used matches or Discard Message, and the order the rules ran in: a mailbox whose filters have one of those, or a Stop processing rules, is worth comparing with its Filters tab. A forwarder on an address that is also a mailbox is missing too: recreate it naming the mailbox first, corepanel mail forwarder set info@example.com info@example.com copy@other.example, so the mailbox keeps its mail.

  • A caching plugin’s browser cache rules no longer count as lost. ExpiresActive, ExpiresByType and ExpiresDefault — the block W3 Total Cache, WP Rocket and most speed guides write into .htaccess — are now applied by CorePanel’s web server to a site’s static files. The check stopped listing them among the lines that “will stop having an effect”, so a site whose only such lines were these no longer shows up in that note.

If your server was transformed before this, those lines were left where they were, and nothing needs doing: once the server runs a CorePanel release that applies them, they take effect on their own. The lifetimes they ask for then win over the panel’s browser cache settings for the files they cover — see browser caching.

  • The check refuses operating systems the transformation has never run on. Until now any EL 8–10 passed. Now only AlmaLinux, Rocky Linux and CloudLinux 8 or later do. RHEL, Oracle Linux, CentOS Stream and other rebuilds get an os.unsupported blocker, which tells you to convert the server to AlmaLinux or Rocky first. Each of them brings its own repositories and subscription tooling into the package installs PREPARE has to get right, and none had been tested.

  • Accounts with a dedicated IP are now a blocker (ip.dedicated). CorePanel serves every account on the server’s main address and sends all mail from it. Earlier builds said nothing, and counted a domain on a dedicated IP as pointing at this server. After the cutover, those domains would have started sending mail from the main IP, so their PTR and SPF records stopped matching. The check now names each account whose IP is not the main one, and each domain in /etc/mailips that sends from another address.

  • Accounts outside /home are now a blocker. This covers accounts spread over more than one home directory, as in /home plus /home2 (homedir.multiple), and accounts all under a single directory that is not /home (homedir.nonstandard, until now a warning that said the layout was supported). CorePanel sets quotas on the filesystem that holds /home, so accounts anywhere else would have arrived with no working quota. The check now reads where each home actually is from /etc/passwd. Earlier builds only looked at WHM’s HOMEDIR setting, which says where new accounts go, and missed a second partition entirely.

If your server was transformed before this on another EL rebuild, with dedicated IPs or with homes on a second partition, nothing here changes it. Check that the domains which had a dedicated IP have SPF records naming the main address. Accounts outside /home have no working disk quota. Moving them under it is a manual job.

  • The edition check knows that Pro now has a cap of its own. CorePanel Pro became Business with an allowance of 50 websites in this release; until now Pro had none. A server with more than 20 websites still gets the edition.websites warning, and its advice now names the trial that would actually hold it: Pro up to 50, Business above that. Earlier builds said Pro had no cap and suggested a Pro trial to any server.

  • The guided run only offers a Pro trial when Pro can hold the server. When the import stops because the edition is too small, the wizard offers a free 21-day trial and runs it for you. On a server with more than 50 websites it now offers Business alone. Before, picking Pro there spent the one trial this server and this address get, and the import was refused a second time. If an older build did that to you, the edition can still be changed mid-trial from Server → License in the panel, at no cost and without restarting the trial’s clock.

  • The WHM API layer and account suspension name the right edition. The messages that said they need Business now say Pro or Business, which is what the panel enforces.


  • Cron jobs keep running after cleanup. cPanel loads its brute-force module, cPHulk, from the operating system’s own PAM files (/etc/pam.d/system-auth and password-auth). Removing cPanel deleted the module and left those lines behind, and a PAM module that cannot be loaded fails the whole stack. Because crond authorizes each user through that stack before it runs their jobs, no account’s cron job ran again after cleanup. The only trace was FAILED to authorize user with PAM (Module is unknown) in /var/log/cron. On the server where this was found, a billing system’s cron went twenty-five days without running. cleanup now removes the lines that load a cPanel PAM module once that module is gone, and keeps the originals in /var/lib/corepanel-transform/pam.d-before-cleanup/.

  • Cron jobs that called cPanel’s PHP by path are rewritten. A job written as /opt/cpanel/ea-php81/root/usr/bin/php -q cron.php, or that used /usr/local/bin/ea-php81 or /usr/bin/php, kept working until cleanup removed that binary, and from then on failed on every run. The import now rewrites those calls to a bare php, which runs the account’s own PHP version with its private ~/tmp and the job’s memory limit. Each rewritten job is listed in the import report, because a job that pinned a version other than the account’s now runs on the account’s.

    If your server was transformed before this build: run corepanel-transform cleanup again with the new build. On a server where cPanel is already gone, it only repairs what the removal left behind, and that now includes the PAM files. For cron jobs, look for cPanel paths with corepanel cron list <account> and fix each one with corepanel cron update. A job that only needs PHP can use a bare php.

  • Bandwidth limits arrive as cPanel had them. cPanel keeps an account’s monthly bandwidth limit in bytes, and the import read it as megabytes, so every limit came over 1,048,576 times too large: a 10 GB plan became a 10 PB one. Nothing was cut off — CorePanel measures bandwidth, it does not enforce it — but the figure the panel showed, and the one it handed to billing, was wrong. The import now converts it. Disk quotas were never affected: cPanel stores those in a different unit, which was always read right.

    If your server was transformed before this build, upgrading the panel corrects it for you: every limit of 1 GB a month or more is divided back by itself when the new panel starts. A limit under 1 GB cannot be told apart from a large one set by hand, so it is left as it is — check the bandwidth limit of any account that had one in the account’s settings.

  • The trial the wizard offers is the trial you get. When the server has more sites than CorePanel Personal allows, the wizard offers a free trial of Pro or Business; it said 14 days, and the trial lasts 21.

  • SSH access is carried over now, not withdrawn. Until this build the transformation had nothing to say about SSH: accounts whose shell was cPanel’s jailshell were repointed to /sbin/nologin when the packages came off, the keys in their ~/.ssh/authorized_keys were never read, and the run told you they had lost SSH and suggested giving it back one usermod at a time. That was the safe thing to do while CorePanel had no SSH of its own. It has one now, and the import uses it.

    Every account is enrolled from the login shell it has on the server. jailshell becomes CorePanel’s sftp level — file transfer inside the account’s own jail, no shell — because that is the nearest thing to a jailed shell that exists today, and because SFTP is what almost every customer on jailshell was actually using. cPanel’s noshell becomes sftp as well: it refuses a shell but serves SFTP, with a key or a password, so an account WHM shows with shell access disabled is still transferring files that way — and on a typical cPanel that is most accounts. nologin and /bin/false become off: those accounts could not log in on cPanel at all, and a migration does not hand anybody something they did not have. An account on a real, unjailed shell — /bin/bash and friends — is left exactly as it was, and the run names it (see below).

  • No account’s SSH access changes until the cutover. Before importing, PREPARE holds the server’s SSH: what the import finds is recorded, and none of it reaches sshd, the key files or the accounts’ groups until the cutover ends the hold and puts the lot in force in one pass. CorePanel’s sshd configuration is installed with CorePanel at the start of PREPARE, but its rules apply only to accounts CorePanel has enrolled on the system, and during the hold that is nobody. While held, the panel’s SSH page says so, corepanel account ssh status shows In force: NOT YET, and changes to the level, the password or the keys are refused — WHM is still what decides, so make them there; corepanel account ssh reconcile --apply-deferred ends the hold by hand if the cutover’s SSH step could not. This is the point worth knowing before you book the window: enrolling accounts while cPanel is still running the server would repoint sshd at CorePanel’s key file and replace a customer’s jailed shell with SFTP-only days before you expected anything to move. Until the cutover every account logs in exactly as it does under cPanel today; the one thing of CorePanel’s that acts on SSH from the install on is its SSH guard, which bans addresses that keep failing. It is also the difference between one sshd reload and one per account on a box with two hundred of them.

  • The cutover applies cPanel as it is then, not as PREPARE saw it. PREPARE can run days before the window, and cPanel keeps serving in between. So before it puts anything in force the cutover reads every account again — its shell, the keys in its ~/.ssh, sshd’s password setting — and a key removed from a home in the meantime does not come back, one added is not lost, a from= restriction added to a key is kept, and an account switched to nologin is not given SFTP. This happens before the homes are tightened to 0700, so a home cPanel left group-writable is still judged as sshd judges it today.

  • rollback gives SSH back too. Its first step, while CorePanel is still running, removes the password grants CorePanel wrote into sshd and takes every account out of CorePanel’s SSH groups and jails, so cPanel’s jailshell and the keys in the homes decide again — except accounts CorePanel shut or suspended after the cutover, which stay shut — and holds the server for the next attempt. An account cPanel still lists as suspended gets cPanel’s own suspension back on its system user (the /bin/false shell, the locked password, the cpanelsuspended group), so WHM and the machine agree and unsuspending it in WHM reopens it as usual. Once CorePanel is stopped it also removes its SSH guard’s ban table from the kernel: nothing would be left to expire those bans, so an address banned during the window would otherwise stay unable to reach SSH on a server cPanel runs again, with no cPanel tool able to show why. A hand-back that could not finish stays in the procedure for the next rollback run (point it at a non-default core with --core-socket); the accounts kept shut are named with the command that reopens one under cPanel. cleanup refuses while SSH is still held, or while corepanel-core cannot say whether it is.

  • A suspended account gets its SSH back when you lift the suspension. cPanel’s suspension does more than flag the account: it replaces the login shell with /bin/false, puts the user in the cpanelsuspended group that cPanel’s own sshd settings deny, and locks the password in /etc/shadow. The level is now decided from the shell the account had before it was suspended, which cPanel keeps in /var/cpanel/suspendinfo (and pkgacct in the archive), so a suspended jailshell account is recorded at sftp and a suspended bash one at legacy — closed while the suspension lasts, like any suspended account. Lifting the suspension in CorePanel undoes cPanel’s three marks on the system user and puts the account back where it was. An account WHM suspended after PREPARE — which CorePanel imported active — is the exception the cutover will not decide for you: cPanel’s suspension stays on it, so its SSH stays closed, and the run names it with the two ways out: corepanel account unsuspend <account> lifts cPanel’s suspension here (not gated on the edition, so this state always has a way out), and corepanel account suspend <account> — Business — suspends it in CorePanel too if the suspension stands.

  • The keys in every home are imported, and that is what keeps the migration from locking people out. The moment CorePanel takes sshd over it points AuthorizedKeysFile at a root-owned tree, so a key sitting in a customer’s home stops working. Each account’s ~/.ssh/authorized_keys and authorized_keys2 are read as the account, never following a symlink out of the home, and each valid line becomes a key in the panel with its options (from=, command=, no-pty) preserved. The files themselves are left untouched.

    A line that cannot be carried is named with its reason, never counted away: DSA keys, RSA keys under 2048 bits and lines that do not parse. Most of those would be refused by the sshd on a modern AlmaLinux or Rocky in any case, but you find out during the run rather than from the customer.

    A home that sshd itself would refuse is refused here too, and named. StrictModes makes sshd ignore an authorized_keys whose file, ~/.ssh or home directory is writable by group or by everyone — so the keys in it are granting nobody anything today. Importing them would copy them into the root-owned file sshd reads unconditionally and make them work, which on a home somebody once ran chmod -R 777 over means a neighbour’s key becoming that account’s access. The run says which homes were skipped and why. On a server whose sshd has StrictModes no those keys do log in today, so they are imported.

  • Password login survives where this server already allowed it. cPanel lets an account with a shell log in over SSH with a password whenever the server’s sshd is configured for it, and people rely on that — FileZilla over SFTP is the everyday case. The import asks sshd itself (sshd -T, before the account is enrolled, so the answer is still this server’s own) and keeps the grant for the accounts it allowed — asking about a connection from the server itself and from the internet, so a Match Address block that allows passwords only from a private network is named instead of becoming a grant that works from everywhere. The run lists them by name, with the one line that revokes one after the cutover: corepanel account ssh password deny <account>. Accounts created on the server afterwards are keys-only, as a fresh CorePanel install has always been. This follows the rule the transformation already followed everywhere else: preserve what cPanel relaxed, harden only on a fresh install.

  • An account that kept its own shell is named, and the run says what that means. Those accounts are recorded at a level called legacy, which means CorePanel is deliberately not touching them: sshd goes on reading their own ~/.ssh/authorized_keys, exactly as it did before. The consequence matters and is easy to get wrong — for those accounts a key you remove in the panel keeps working, because the panel’s list is not what sshd reads. corepanel account ssh set <account> --level sftp is what takes it over. Cutting an unjailed shell during a migration, unannounced, is not this tool’s call to make; saying so clearly is.

  • The cleanup no longer tells you those accounts lost SSH, or offers a repair that would not work. It used to say their shell was gone and suggest usermod -s /bin/bash <account>. Both halves were wrong once CorePanel owns sshd: what an account has is its access level, not its shell, and usermod grants nothing. The cleanup does not guess in the other direction either — it does not claim their SFTP is fine, because whether it is depends on the level each account ended up at. It says what the field does and does not decide, and points at corepanel account ssh status <account>, which answers per account. What a missing shell really costs is su - and an interactive login.


  • A MariaDB older than 10.11 is transformed, not refused. The check used to block any server below 10.11 and send you to WHM for a one-way database upgrade before you had even decided to migrate. That floor was wrong: it was the version a fresh CorePanel installs, not the version a takeover needs. The real line is 10.4, where MariaDB gained multi-authentication — the takeover gives root a socket login beside the password cPanel keeps using, both at once, which is what keeps the rollback real. Below 10.4 root can hold one method or the other, so letting CorePanel in would take cPanel’s own access away, and it stays a blocker there.

    A 10.4, 10.5 or 10.6 is now adopted exactly like a 10.11: same data directory, same server, same version afterwards, same seconds whatever the databases weigh. Nothing is reinstalled and no repository is swapped. The check says nothing at all about it — no note, no warning, no score — because there is nothing for you to do about it. cPanel never moves a server to a new MariaDB major on its own — its maintenance run only warns — so a server stays on the one it was installed with until somebody upgrades it, and 10.11 did not exist before 2023. Measured end to end — check, cutover, verify and rollback — on a cPanel 11.136 server running MariaDB 10.5.29, where the database step took 1.1 s.

  • The refusal below 10.4 says what it is refusing on. It names cPanel’s own tool (WHM » Database Services » Upgrade Database Version, or whmapi1 start_background_mysql_upgrade version=10.11), points at 10.11 rather than 10.4 because cPanel’s menu has no 10.4 entry, and explains the reason in one line instead of citing a requirement.

If your server was already transformed, or you already upgraded: nothing to do, and nothing to undo. A server that got through CUTOVER was adopted on whatever it ran, and that has not changed. If you upgraded to 10.11 because an earlier build of this tool insisted, you did not do anything wrong — 10.11 is the version a fresh CorePanel installs, and it still receives security fixes, which 10.6 stopped receiving in July 2026. It simply is not a requirement any more.


  • CloudLinux 8, 9 and 10 are transformed with CloudLinux left installed. Until now the check stopped on any CloudLinux server and asked you to remove it first: a kernel swap and a reboot, in a window of their own, before the one the transformation needs. The first real server to run the check left at that point. The transformation now takes the server over with CloudLinux in place, and the check lists it under Handled by the cutover (os.cloudlinux_stays, a note that costs the score nothing). Measured on a CloudLinux 9.8 server with CageFS on every account, transformed and rolled back twice:

    • CageFS is switched off in the cutover and back on by rollback. Inside it, an account’s cron jobs and SSH sessions see CorePanel’s PHP without its configuration, so a PHP cron job would run with no extensions at all.
    • The kernel and LVE are left alone, so there is no reboot. LVE has nothing left to limit: corehttpd and the PHP-FPM pools never enter it, and CorePanel enforces its own per-account limits.
    • Mail and FTP work under CloudLinux’s /proc. CloudLinux mounts /proc with hidepid, and CorePanel’s authentication broker, which identifies Postfix, Dovecot and pure-ftpd by looking them up there, refused all three. The result was every incoming message deferred and every mailbox and FTP login refused, while the health gate reported green. The broker installed by this release accepts them, and the same fix applies to any server whose /proc is mounted that way.
    • VERIFY no longer marks PHP as broken over clos_ssa, CloudLinux’s Slow Site Analyzer extension. It only reports to CloudLinux, and no site calls it.
    • Accounts on the PHP Selector keep the version they picked there, with the extensions they enabled. cPanel’s configuration only names the server’s default for their sites, and that was all the transformation read. On the test server, three accounts whose sites answered from PHP 7.4, 8.0 and 8.3 were all planned onto the default 8.1. PREPARE also read alt-php’s extensions from the wrong place: as root, outside CageFS, where alt-php 7.4 loads only its built-in modules. So nothing was carried over, and an account that loaded ionCube, imagick and redis would have arrived without them. Each account’s choice is now read from its own CageFS, as that account, because those files belong to the account. The check lists these accounts under Carried over (php.selector), and warns about the settings changed in the Selector that are not carried over (php.selector_options). memory_limit is carried: see the next entry.
    • The check’s PHP: line lists only the versions that are installed. It counted every directory shaped like a PHP version, and CloudLinux leaves empty ones behind. The test server showed 24 versions and had 5.

    Two CloudLinux cases still stop the check, each with its way out. CloudLinux 7 is EL7 underneath and still has to be removed first. MySQL Governor (mysql.governor, new) replaces the database server with CloudLinux’s own builds, which the transformation has never been run against; CloudLinux’s mysqlgovernor.py --delete puts the original back. See CloudLinux.

  • A site’s larger memory_limit comes across. CorePanel holds every account to one memory_limit, pinned so no site can raise its own. A site that had raised it on cPanel — in the version’s php.ini, in the .user.ini the MultiPHP INI Editor writes into its docroot, or in CloudLinux’s PHP Selector — arrived on CorePanel’s 256 MB and ran out of memory, and its .user.ini no longer helped. PREPARE now raises CorePanel’s limit to the largest any site ran with, before the accounts are registered. It never lowers it, and caps it at half the server’s RAM, because most of those files are written by customers. The check lists the raise under Carried over (php.memory_limit) with the site it came from. It warns instead about a site that ran with no limit at all (php.memory_unlimited), and a server too small to raise it (php.memory_limit_kept).

  • Sites on PHP 8.0 keep it, and on EL8 so do sites on PHP 5.6 and 7.0 to 7.3. CorePanel now installs these branches from Remi for a migrated site that runs one of them. Before, it installed none of them, and the importer gave such a site the newest installed version of its own major, otherwise the newest version installed: 8.0 went to 8.4, and 5.6 to 8.4 as well. Remi publishes 5.6 and 7.0 to 7.3 for EL8 only, CloudLinux 8 included; on EL9 and EL10 a site on one of them still moves. The check now names that move for each site, with the version it will run (php.unsupported), and PREPARE names the same version. Both used to be wrong about it. The check listed 8.0 as supported and quoted “the nearest supported version” for the rest, a rule nothing applies: on the test server, an account on 8.0 was told its sites would stay on 8.0, and they came up on 8.4. The branches before 7.4 and 8.0 are end of life, and the panel badges them that way. They are there so the move off an old PHP happens after the migration, not during it. The extension catalogue gains mcrypt, which software written for those branches still calls.

  • PREPARE carries shmop and the System V modules across, and stops reporting as lost the modules that arrived with another one. The posix extension installs shmop, sysvmsg, sysvsem and sysvshm from the same package, but a source loading shmop — every ea-php and alt-php build does — was told CorePanel could not install it. And msgpack, which comes with redis, was reported as not carried over on servers that ended up loading it.

  • The CloudLinux removal the check prints for CloudLinux 7 now works on the cPanel you actually have. The copy it carried had gone stale: it restored Apache with /usr/local/cpanel/scripts/easyapache --build, which is EasyApache 3. That file does not exist on any cPanel this tool supports, so the step failed with No such file or directory just when the sites that ran on alt-php had no interpreter, and Apache was left configured for a CloudLinux module that was being removed. The procedure now matches CloudLinux’s current documentation: it removes the LVE packages (yum remove lve kmod*lve*) and reinstalls cPanel’s default EasyApache 4 profile with ea_install_profile before the reboot. It also asks you to check the bootloader first, and it says the sites come back on that profile’s PHP versions. See Removing CloudLinux.

    If you removed CloudLinux with the earlier procedure, run the yum remove and ea_install_profile lines from that section before running the check again.

  • A rollback no longer leaves Exim bouncing the mail of every domain on the server. The cutover removes cPanel’s mail, FTP and DNS packages, and rpm sets their edited configuration files aside as .rpmsave. The rollback reinstalled the packages and let cPanel rebuild its configuration, but /etc/localdomains, the list of domains Exim delivers for, is not a file cPanel rebuilds. Reinstalling Exim wrote a new one naming only the server itself, so after a rollback every hosted domain was treated as remote and its incoming mail bounced, while every port answered and the rollback reported success. The cutover now copies the configuration files of the packages it removes into /var/lib/corepanel-transform/cpanel-config/ first, and the rollback puts them back, with their original modes and owners, before cPanel’s rebuild.

    If your server was cut over with an earlier build, there is no copy to put back: a rollback run with this build says so and names the file. Before rolling back, keep /etc/localdomains.rpmsave; after it, if /etc/localdomains lists only the server’s own name, cp /etc/localdomains.rpmsave /etc/localdomains puts the domains back. If you already rolled back, check /etc/localdomains now.

  • Running the cutover again after a rollback no longer takes the sites down and then stops. A rollback ends by stopping and disabling CorePanel’s own services, and tells you to run run --phase cutover to try again. That cutover stopped cPanel, Exim and Apache, then failed in its tenth step because corepanel-core was not running, with every site on the server offline. The cutover now starts and re-enables CorePanel’s services in its first step, before it stops anything of cPanel’s. If one of them will not start, the cutover stops there, while cPanel is still serving every site.

    If a re-run already failed this way on your server, run systemctl enable --now corepanel-sys corepanel-core corepanel-auth corepanel-api, then corepanel-transform resume.


  • A MariaDB older than 10.11 is now a blocker in the check, with its way out, instead of a promise the run could not keep. The report used to say such a server would be “upgraded in place during CUTOVER”, and the plan priced that step in minutes. No build of the tool has ever performed it. The run stopped at the database step, after cPanel’s own services, mail and FTP had already been stopped, and left the operator to start them again. cPanel still installs 10.3, 10.5 and 10.6, and all of them have reached end of life, so this was not a rare server. The check now stops before anything is planned and names cPanel’s own procedure: WHM » Database Services » Upgrade Database Version, 10.11 or newer. The upgraded server is then adopted as it stands, in seconds. MySQL is a blocker too, for the same reason: the dump and restore the report described does not exist. See Databases.

  • CUTOVER refuses to start on a database it cannot take over. A plan written by an older build still says “upgrade-in-place”. So does a plan that could not read the engine from disk, if the running server then turns out to be too old. Either one now stops before the TRANSFORM prompt, with nothing stopped. If the refusal happens to you, upgrade MariaDB as above and run corepanel-transform plan again. The new plan starts a new run: run asks you to set the old state file aside, and PREPARE goes over what it already installed a second time.

  • A 2 GB server is no longer refused for having 2 GB. PREPARE’s preflight compared the kernel’s MemTotal against exactly 2 GiB. But MemTotal is what remains after the kernel’s own reservation: our 4 GB test server reads 3,559 MB, so no 2 GB server could ever reach 2,048, and every one was refused for meeting the minimum it was checked against. The thresholds now sit between server sizes, so a 2 GB server passes and a 4 GB one is not told it is short. The question is also asked earlier: the check reports it as a blocker below 2 GB and as a note below 4 GB, so it comes up before a window is booked rather than at the start of PREPARE. See Memory.

If your server was transformed before this: nothing here reaches it, and nothing needs to. A server that got through CUTOVER was running MariaDB 10.11 or newer, because on anything older the run stopped at the database step, and it had at least 2 GiB of RAM.

Both of these live in the panel rather than in the tool, so a server already transformed with 1.6.7 gets them by updating CorePanel; the migration binary itself is unchanged.

  • The run no longer asks you to reset the passwords it just saved. A suspended account adopted in place carries a closed /etc/shadow entry, which the run read as an unusable password and turned into “the account needs a password reset” — seven of them on the test server, every one a password that had in fact been preserved. Following that list would hand seven customers a new password none of them asked for. When the password was kept, the run now says so.

  • An account of a reseller shows the plan it is on, even when the plan is yours. cPanel puts a reseller’s own plans in the name, so a plan with no reseller prefix stays with the server owner while the accounts on it belong to their resellers — which is most accounts on a migrated server. The account list looked the plan up under the account’s own reseller and nowhere else, so those rows showed no plan at all while being attached to one the whole time.

  • Lifting the suspension of a migrated account gives its owner back their own password. An account that arrived suspended arrived correctly — with the source’s own date and reason, its sites serving the suspension page — and then could never be reopened properly: the panel answered “invalid credentials” to the password its owner had been using for years, and its mail refused it too. Two states did it, and neither was the suspension. The account row was given a random password, because every locked credential was treated as unrecoverable; and each mailbox and FTP login was written closed, which no unsuspend undoes — an unsuspend only reverses what the suspension did. Nothing was gained by either: on this panel the account’s own suspension is what closes those logins, which is why suspending and reactivating from the panel has always returned everything intact. Now a login the source closed because the account was suspended arrives open with its password kept, and the suspension does the closing. A login the source closed on its own, under an account that was running, still arrives closed. The system user keeps getting a fresh password: its hash lives in /etc/shadow, where nothing reads a suspension.

  • A reseller your old panel had cut off no longer arrives able to trade. Only its accounts came across suspended; the organisation itself came across active, so giving it its first administrator handed a working login to a provider you had suspended — and said nothing, because nothing knew. cPanel has no reseller-level suspension to read: suspending a reseller there suspends its own hosting account and then each account it owns, and that account’s suspension is now what the import reads. It is the same credential that signs in to WHM, so a reseller whose account was suspended could not get in there either. Its ceilings still come across, because a suspended reseller is one you may want to lift.

  • A reseller’s ceilings no longer stop the migration that brings them across. The two aggregate limits 1.6.5 started importing were also enforced during the import, which reads well and stops the ordinary cPanel server: a reseller with resource limits on, and a plan whose QUOTA is unlimited — cPanel’s own default — hit the rule that a capped reseller may not be given an unlimited account. PREPARE then stopped in the worst possible place, with CorePanel installed, some accounts registered and the rest not, on a wall the operator cannot lower from the source panel because the account already exists there. A migration reproduces a server that is already running, so both ceilings are now reported on the way in and enforced everywhere else: creating an account, moving one between resellers and changing a quota all still refuse. A reseller that arrives above its own ceiling is named in the run, and refuses everything until it comes back under it.

  • A suspended account’s mailboxes keep the passwords their owners have. cPanel closes a login by writing a marker in front of the hash it keeps — !!$6$… in /etc/shadow for the account, and *LOCKED*$6$… in ~/etc/<domain>/shadow for each of its mailboxes. The importer knew about the first spelling and read the second as a marker that had replaced the password, so every mailbox of every account suspended at the source arrived with a random password instead of its own. Lifting the suspension then handed the customer back a mailbox that no longer took the password they had been using, which is not what lifting a suspension means anywhere else. Both spellings are now unwrapped, and the mailbox arrives closed with its own password behind it.

  • A reseller that owns its own account is no longer reported as a nested owner. WHM writes acmehost: acmehost into /etc/trueuserowners when it makes an account a reseller that can manage its own hosting, and the check read that as ownership nested one level deep: a warning saying the mapping had to be confirmed by hand, a score of 90 instead of 100, and a line spelling it out as “acmehost (under acmehost)” — the same name on both sides of “under”. Real chains, where a reseller is owned by a different reseller, are still reported and still flattened.

  • A site that was already broken no longer blocks the cleanup. The report knows how to tell “this was serving an error before the run” from “this run broke it” — it compares every site against the reading taken while cPanel was still serving — but the reading was only ever loaded on a server whose databases could not be read, which is to say almost never. On every ordinary server a site that had been failing for months came out as a failed check, and cleanup then refused to remove cPanel over it. The cure it offered, “fix it and run verify.report again”, was a cure for a fault the operator did not have. A site serving a PHP fatal under cPanel is now recorded as what it is: broken before the run, and not this run’s doing.

  • Every refusal now hands over the command that clears it. cleanup would say to run verify.report again — which is the name of a step, not something anybody can type. The obvious guess, running the phase, silently does nothing at all, because the runner skips a step already recorded as done. So the one instruction on the one step with no way back described a command that changes nothing, and the operator was left reading the same refusal over the same stale report. Every refusal now prints the exact line, --redo included.

  • What was installed on top of cPanel is removed with it, and named first. A cPanel server is rarely only cPanel, and until this build the extras were only a reason to stop: the step refused when something outside its own package set depended on cPanel, and left working out what to do to you. Now they go with it — found by three rules and no list of product names — and every one is printed before the ERASE-CPANEL prompt, so what you agree to is what happens. Products installed by a script rather than from a repository are named too: no rule about packages can see them, their files go with the tree regardless, and the largest one on a real server was 141 MB.

  • And what was removed is written down, because the bills do not stop when the packages do. /var/lib/corepanel-transform/removed-products.txt lists everything that went. A licence goes on charging for a product that is no longer installed, and the person who can cancel it is usually reading a week later from a terminal whose scrollback is gone.

  • cPanel no longer blocks the deletion of its own directories. Two things it leaves behind defeated a plain removal, and both were found on a real server rather than imagined: nine log files carrying the append-only attribute, which root cannot unlink either — 249 MB stayed put with “operation not permitted” — and /var/cpanel bind-mounted into the VirtFS jails of jailed accounts, 21 mounts for one account, which makes the source busy. The attributes are cleared and the mounts released first. A tree that still cannot go is now reported as work left rather than as a finished step, so running cleanup again comes back for it instead of taking the “already gone” branch forever — which is what happened before, because a partial RemoveAll deletes the very file that branch is chosen by.

  • cPanel’s jails are removed, and the removal refuses to touch them while anything is still mounted inside. An account with a jailshell has a VirtFS jail under /home/virtfs in which /usr, /var, /opt and its own home are bind mounts of the real ones — cPanel leaves a file there warning that deleting the directory destroys the operating system, and it is right, because a recursive delete goes through a mount point. Every mount under a tree is now released before it is deleted, and a tree that could not be fully unmounted is left completely alone rather than partly deleted. With the mounts released, five jails on a real server came to 11.5 MB; with them standing, du reported 13 GB because it was walking /usr and /var through the binds.

  • Accounts are no longer left with a login shell that does not exist. jailshell and noshell live inside /usr/local/cpanel and go with it, and every account still pointing at one was left unable to use ssh or su -: ten out of ten accounts on the server this was measured on, and the failure shows up the day after the migration. They are repointed to /sbin/nologin and listed. FTP is unaffected — CorePanel authenticates it through its own service, which never looks at the shell — and ssh is withdrawn rather than widened on purpose, because jailshell’s jail went with cPanel and /bin/bash would hand those accounts more than they had the day before. Give it back deliberately with usermod -s /bin/bash <account>.

If your server was transformed before this, only the last two reach it, and only if you have not run cleanup yet. If you have, the step is already recorded as done, so it is skipped unless you ask for it explicitly:

Terminal window
corepanel-transform run --phase cleanup --redo

That re-enters a cleanup with nothing left to remove, which is the branch that now clears the attributes, releases the mounts and deletes whatever stayed behind. Check what it found against lsattr /usr/local/cpanel, mount | grep cpanel, and awk -F: '$3>=1000 {print $1, $7}' /etc/passwd for accounts pointing at a shell that is gone. The products installed on top of cPanel are not removed retroactively — they are still installed, still licensed, and worth checking against what you are being billed for.

  • Your resellers now arrive with the limits they were sold on. cPanel writes what a reseller may sell into /var/cpanel/reseller-limits.yaml — an account count, a disk total, and whether they may oversell that disk — and until this build nothing read it. A reseller was created from the accounts they owned and took CorePanel’s defaults, which mean unlimited: a provider capped at 25 accounts and 50 GB on the old panel woke up able to sell without limit, on the very servers that arrived with those two numbers already written down. There is a new PREPARE stage, “File the resellers this server sells through”, and it runs before the accounts on purpose — an account’s owner is created the moment that account is registered, so a reseller filed afterwards would have spent the whole import uncapped. A reseller who had limits but no accounts comes across too. Both ceilings are enforced from the first account charged against them.

  • A reseller’s own hosting plans are now theirs. cPanel has no owner column for a package: it puts the owner in the name, as acmehost_starter. Filing a whole server’s plans under one seller therefore kept the prefix and lost the owner — every reseller’s plans belonged to the server owner, were not theirs to edit, and the Packages screen showed one operator owning a list of acmehost_* rows. Each plan whose prefix names a reseller this server now has is filed under that reseller, with the prefix removed: it becomes their starter. Two resellers’ starter and yours are three different plans, because a plan is unique per owner and not per server. A prefix matching no reseller is left exactly as it is — gold_plus on a server with no reseller called gold is a plan somebody named with an underscore, and moving it would take one of your own away.

  • What a cPanel reseller carries and CorePanel does not model is printed, per reseller, instead of vanishing. Three things: the per-reseller privilege list, the total bandwidth ceiling, and the restriction on which packages they may sell. The privileges matter most, and in the opposite direction from everything else on this page — a reseller who could not terminate accounts or touch DNS on cPanel can here, because a CorePanel reseller is all-or-nothing within their own accounts. It is the one thing a migration widens rather than narrows, and now it is on screen while you are watching the run.

  • And the privilege list is now kept, so it comes back when it can still change your mind. Printing it during the run is not enough on its own: a reseller that arrives has no panel login and can do nothing at all, so nothing actually widens until somebody gives it one — days later, with the run long scrolled away. The set the source granted is stored on the reseller, corepanel admin create --seller acmehost answers with it at the moment that login is handed over, and corepanel seller show acmehost prints it afterwards. The administrator is still created: it is a note, not a refusal, because widening those privileges may well be what you intend. A reseller cPanel granted everything is never mentioned — nothing was withheld from them to begin with.

  • A billing system can provision on behalf of a reseller. WHM’s createacct takes an owner=, which is how a provider’s own WHMCS creates an account for one of their resellers, and the compatibility layer used to read every parameter except that one — silently landing all of those accounts on the server owner. It is honoured now, it counts against that reseller’s ceilings, and an owner naming nobody on the server is refused instead of being quietly reassigned to you.

  • A plan written by an earlier build is refused, as always, and this is one of the times it matters. The new stage means older plans do not list it; re-run plan before you start. A run already under way is unaffected — the guard only stops a plan whose step list is missing something the binary would otherwise skip in silence.

    This build writes plan schema 10.

If your server was transformed before this: its resellers are here, with their accounts and their ownership intact, and with no ceilings — they can sell as much as they like. Their plans are on your own account, still called acmehost_starter. Nothing in a newer build reaches back to fix either, and the source is usually gone by now.

The plans can be left as they are: accounts stay attached to them and keep working, and a re-run of the import still finds them there. Moving one is a rename in the panel plus a change of owner, and only worth it if the reseller needs to edit their own plans.

The privilege lists are gone with the source panel and no build can recover them. What is worth knowing is the rule itself: any reseller on that server whose cPanel privileges were restricted now has all of them within their own accounts, and the way to check who has a panel login at all is corepanel admin list --all.

The ceilings are the part worth fixing, and it is two commands. Set the numbers from the figures the old panel had:

Terminal window
corepanel seller list
corepanel seller update acmehost --max-accounts 25 --max-quota 51200

--max-quota is in MB, and 0 on either one means unlimited. Add --overselling for a reseller who was allowed to oversell disk on cPanel. corepanel seller show acmehost then prints what they have sold against each ceiling, which is how you check the numbers are the ones you meant.

  • A transformed server keeps talking to the outside world from the IPv4 its licences were issued against. The cutover stands your firewall down — pxf, CSF, whichever one is in front of the machine — and on a dual-stack server that quietly changes something nothing on the box announces: with no policy in /etc/gai.conf, the C library prefers IPv6 to IPv4 for every destination that offers both. A server that had been seen at its IPv4 for years starts being seen at its IPv6 on the first connection after the window, and everything licensed against the address — WHMCS, Softaculous, LiteSpeed, JetBackup, Imunify — begins failing its licence check. It does not fail at the cutover, which is what made it hard to place: each product runs on a cached verdict for hours, so the first symptom arrives in the middle of the night, on a server whose migration went perfectly. The cutover now writes that preference back, in a marked block, immediately after the firewall goes down and before the PHP pools start — the order matters, because the C library reads the file once per process. A server with no IPv6 route out is left untouched, and so is one whose /etc/gai.conf already says something: if you have set a policy of your own, the run tells you what to re-check instead of overruling you. The block is delimited, so a rollback takes back exactly what the run wrote — and removes the file only if the run is the one that created it.

  • If your server was transformed before this: it is still preferring IPv6, and any licence tied to your IPv4 will fail at its next revalidation if it has not already. Put the preference back by hand and restart what is running, which takes a minute:

    Terminal window
    echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf
    systemctl restart 'php*-php-fpm'
    curl -s https://ifconfig.me # must answer your IPv4, not an IPv6 address

    Then make each product check its licence again rather than waiting for it — in WHMCS, that is one visit to /admin/ once the preference is in place. A licence that has already been marked invalid comes back on its own when the address it sees matches the one it was issued for; you only need your vendor’s reissue if the address itself has genuinely changed.

  • Applications that send mail unencrypted to their own server go on working. cPanel’s Exim offers AUTH on port 587 without asking for TLS first; CorePanel’s installer does the opposite, because requiring TLS is the right default for a server nobody has migrated anything onto. In between sits every application already on the machine — a billing system, a WordPress plugin, a shop — configured against Exim’s behaviour, and from the cutover onwards each one stopped sending mail with “SMTP Error: Could not authenticate.” Nothing was wrong with the credentials: Postfix simply does not advertise AUTH until the session is encrypted, so the client found none and gave up. The only trace is a connection that says EHLO and hangs up, which is why the first server this happened on went a day without an explanation while its payment confirmations failed. The cutover now reads what your mail server advertises before it stops it, and configures Postfix to match: a server whose Exim allowed AUTH in the clear keeps allowing it, and one that already required TLS stays strict. STARTTLS is offered either way, so mail clients go on encrypting. A CorePanel server installed from scratch is unaffected — the strict default stands there.

  • If your server was transformed before this: its applications are still failing to send. To put it back the way your Exim had it:

    Terminal window
    postconf -P 'submission/inet/smtpd_tls_security_level=may'
    systemctl reload postfix

    The better end state is to move those applications onto STARTTLS and run the same command with encrypt; this is the switch that buys you the time to do it.

A build whose changes are all in the code the tool shares with the panel, and all of them land on the mail of a server being transformed.

  • A mailbox cPanel had suspended arrives still receiving mail, with only its sign-in shut. cPanel suspends a mailbox by locking its password hash: the address goes on accepting mail and nobody can read it until the login is opened again. CorePanel had a single switch for both halves, so the import had to pick one — and it picked shut, which meant every suspended mailbox answered 550 User doesn't exist from the moment of the cutover. Mail cPanel had been accepting the day before was refused at the door, for exactly the addresses whose owner was most likely to be away. A mailbox now carries the two halves separately: the import writes delivery open and sign-in closed, corepanel mail list reports that state as login_disabled, and the panel draws it in amber rather than as either healthy or suspended.

  • If your server was transformed before this: those mailboxes are still shut for delivery, and still bouncing. corepanel mail activate <address> reopens them — both halves, so the sign-in cPanel had closed comes back with it. If it has to stay closed, give the mailbox a new password its owner does not have.

  • A forwarder that also keeps a copy in its own mailbox is imported instead of being skipped. cPanel expresses that arrangement — a role address somebody still reads while a manager or an archive gets a copy — by listing the address among its own destinations, and CorePanel refused it outright, for the reason any other forwarder on a mailbox’s address is still refused: it would divert every message away from the mailbox. So each one was dropped, named in that account’s import report as a skipped forwarder and nowhere else. The mailbox went on receiving and every other destination quietly stopped getting its copy — the kind of loss nobody reports for weeks, because the person who notices is the one who was only ever meant to be reading along.

  • If your server was transformed before this: the forwarders were not created and nothing has created them since. corepanel mail forwarder set <address> <address> <other destination> — the source listed among its own destinations — restores one, or add it in the panel with the address itself among the destinations.

  • The zones a transformation publishes carry the account’s DKIM key. The cutover’s DNS step has been reporting “published without the account’s DKIM key: outbound mail is signed and the world cannot verify it” for every zone on every server, and it was telling the truth. Two defects held each other up: sys generated the key under the domain’s own selector and then read the record back under the service-wide one, discarding the resulting error and returning an empty value; and core refused any TXT value over 255 characters — which is the limit on a single character-string inside a TXT record, not on the record, a sequence of them — while a 2048-bit key is about 400. The first made the second invisible.

  • If your server was transformed before this: the zones are published without the key and the mail leaving that server is signed with a key nobody can look up. corepanel mail deliverability reports the coverage; rotating the key from Email → DKIM in the panel now publishes the record.

  • Port 465 speaks TLS, and a new sender waits five minutes rather than four hours. Both come from the installer the run drives, and a freshly transformed server is where they bite hardest. The submission services were configured by line position, which left every option in the smtps block commented out — smtpd_tls_wrappermode included — so implicit TLS failed its handshake while 587 kept working and hid it. Implicit TLS is what cPanel hands out, so it is what every migrated mail client on that server is already set to. Greylisting, meanwhile, deferred every unknown sender for four hours against Rspamd’s own five-minute default: measured on a migrated server at 34 soft rejects against 19 deliveries in the first fifteen hours, with one real sender retrying for exactly 4h00m before it got through. cPanel’s Exim does not greylist at all, so on the day after a migration that reads as CorePanel is eating our mail.

  • If your server was transformed before this: both are server-wide settings the installer writes once, so an existing server keeps what it was given. Upgrading the packages does not rewrite them.

  • ACME orders stop dialling cPanel’s PowerDNS. The DNS-01 provider is recorded when a domain is first handed to the certificate manager and never re-read, and during a transformation every domain is registered while /etc/pdns/pdns.conf still belongs to cPanel — so once the cutover installed our own PowerDNS, every certificate order after it kept talking to an API that no longer existed. sys now reconciles the managed domains against the nameserver in front of it when it starts, which the cutover makes it do exactly once, at the right moment. A related fix stops the first transient failure from parking a domain in max_retries_exceeded with nothing left to revive it.

  • Hosting packages are imported, and before the accounts. The documentation has always promised packages among what carries across, and the machinery to import them existed, was registered and was tested — but nothing ever called it, so a transformed server ended up with no packages and every account attached to none. Two things had to change together: the packages are read by the tool, which runs as root and can read cPanel’s 0700 package directory, and they are filed before the accounts, because an account is joined to its package by name at the moment it is registered. A server whose accounts were all made by hand has no packages, and that is not a failure.
  • If your server was transformed before this: filing the packages afterwards does not attach the accounts that are already there, and re-applying a package would overwrite limits that came from the account rather than from its plan. Leave them, or set the package per account in the panel.
  • The panel’s login is handed over before the health gate, not after. By the time the gate asks its questions the server is already CorePanel’s — the sites are being served and the accounts are registered — so a gate that stopped over one encoded site used to end the run with the administrator’s password never generated: a transformed server nobody could sign in to, with the rollback as the only move left. The step now runs at the end of the cutover, and the credentials are printed last on the screen whether the run finished or stopped. It sets a password only when there is none, so a second pass never locks out an administrator who has already changed it.
  • Every site is read before the window opens, and compared afterwards. A new first step of the cutover asks every site for its homepage while cPanel is still serving it and writes down what came back. The health gate then asks the better question — is this the same page? — because “the site answers” is not “the site works”: an ionCube-encoded application missing its loader answers HTTP 200 with a notice where the page should be, and a gate reading status codes called that a healthy site. It also means a site that was already broken before the run is named at the start of the window instead of being blamed on the transformation.
  • PHP fatals the response cannot show are reported. With display_errors off, a fatal error after the output buffer has flushed leaves an HTTP 200 with the page cut short mid-sentence — invisible to any check that reads status codes or page sizes. The gate now reads the account’s own PHP error log across the request and reports what landed there, with the file and the line. Always a warning, never a failure: the code is the customer’s own, unchanged by the run, and it was throwing the same thing on cPanel. It also reports a log the account cannot write, since an empty log and an unwritable one look identical.
  • The PHP versions and extensions your sites actually run are installed during PREPARE. Before, an account was given the PHP version CorePanel happened to have, falling back to the newest installed — so sites running cPanel’s 8.1 came back on 8.4, three minor versions up, with nothing saying so. Now every version in use on the source is installed, sites whose vhost says inherit are resolved through cPanel’s own default, and each interpreter’s extensions are compared and the difference installed — imap, soap, bcmath, posix and, above all, the ionCube Loader. A version or extension that cannot be installed is named, with the sites it affects, while cPanel is still serving them.
  • Mailboxes cPanel had suspended no longer fail the cutover. They were counted as mailboxes that failed to log in, which is exactly what a suspended mailbox is supposed to do.
  • cPanel’s dnf exclusion is cleared at the cutover, not at cleanup. With php* still excluded, every CorePanel PHP package looks like it does not exist — a broken repository that is really a one-line exclusion. The rest of that line, which protects cPanel’s own mail and FTP builds, stays until the cutover, and a rollback restores all of it.
  • The panel says cPanel is still installed. After a transformation the dashboard carries a card naming how many cPanel packages are left and roughly what they occupy, with the cleanup command ready to copy. It disappears by itself once cleanup has run. See Dashboard.
  • The contact address and the nameserver addresses are adopted too. A green transformation still opened the panel’s initial configuration assistant, asking for a contact address, a hostname and nameservers it had been handed half an hour earlier — the panel counts a configuration with a blank administrator email or an unaddressed nameserver as incomplete. Both are now taken from the panel being replaced. Each nameserver’s address is resolved from the delegation the server has been serving all along, so a second nameserver that lives on another machine stays there instead of being flattened onto this one. The address is recorded, not acted on: it does not become the ACME contact and it does not rename the administrator until you confirm it in the panel.
  • A transformed server now has a panel login. A transformation never passes through corepanel system init, so the administrator existed with no password at all: a run could verify every domain, mailbox, database and zone and finish on a panel nobody could sign in to. The run now generates the first password, prints the address, the user and the password on screen, and writes the password to /root/.corepanel_password (0600) — the same file a hand-installed CorePanel uses. It never overwrites a password that has already been set.
  • If your server was transformed before this: corepanel system init --admin-email you@example.com --no-interactive prints the address, the user and a password. Leave --domain out — the transformation already set the panel domain.
  • The clock is on screen while the sites are down. Steps now say “6/13” rather than “6.”, the plan’s estimate for a step is printed before it runs rather than after, and from the moment the sites go down the tool shows how long they have been down and how much of the booked window is left. Resuming into a half-finished cutover says which outage it is measuring instead of restarting the clock at zero.
  • The outage is declared by the step that actually opens it. “The visible clock starts here” was written on the step that stops Apache, which is only true when taking over the databases does not restart MariaDB. Where it does, every database-backed site had been serving an error since the step before — for around two minutes — while the run said the window had not opened yet.
  • The published downtime matches the measured downtime. The tool and the documentation said “minutes rather than hours” for a window measured at 28 seconds — ten times worse than the truth, told to exactly the reader who is looking for a reason not to migrate. Both now say seconds, publish the conditions (45 accounts, 51 domains, a 2-vCPU server) and state the scaling law: the window grows with the number of files, not with the size of the data.
  • Pointing the tool at a server that is not running cPanel says so, instead of reporting what it expected to find.
  • A rollback gives back mail, FTP and DNS. Reversing a cutover restored the web and the databases but left mail, FTP and DNS with CorePanel’s daemons — cPanel’s own builds of those same services had been removed during the window. They are now reinstalled and cPanel rebuilds their configuration itself, so what comes back is a whole cPanel.

The release that made the transformation a single guided operation rather than a set of stages an operator drives by hand. It followed the first end-to-end run against a real cPanel server, and most of it is that run’s findings.

  • corepanel-transform with no arguments is the whole transformation. It walks the five stages in order, printing what each will do to this server — what goes offline, for how long, and whether it can be reversed — and waiting for an answer before each one. The subcommands are still there for automation.
  • The plan is priced from a measured run. Every estimate was repriced against real timings, and each constant records what it measured. Where the real cost was never exercised, the old figure was kept and says so.
  • VERIFY became a record rather than a second gate, reporting per account against the inventory taken before anything happened.
  • The rollback became a command. corepanel-transform rollback works the procedure out from the server in front of it rather than replaying a file written a week ago, gives cPanel its scheduled work back, and stops the panel last, once cPanel is answering again. It refuses — naming the difference — when a mailbox, an FTP login or a password has changed under CorePanel since the cutover, rather than discarding it quietly.
  • cleanup is the only one-way door, and the guided run never offers it. It refuses unless verification passed and something is answering on ports 80 and 443 right now.
  • The health gate probes the server from outside — an HTTP request per site, a mailbox login, a database login, an SOA query per zone, an FTP login — instead of asking systemd whether units are running. It also stopped asking the WAF whether the cutover worked.
  • Certificates carry across, and a domain with no certificate name of its own is judged over HTTP.
  • Where every domain actually points is inventoried before anything runs.
  • The three decisions the plan makes now fail closed rather than assuming the comfortable answer.
  • The WHM API credentials are carried across as digests, so the plaintext never leaves the billing system that has been sending it for years, and the API is left closed.
  • The host firewall is turned off rather than left to the administrator, and third-party products are judged by who acts on the server rather than by a severity rating.

The first release in which PREPARE and the cutover ran end to end on a real cPanel server. Everything here exists because that run found it, and none of it is reproducible from a test fixture: several of the fixes exist only because there was something else on the machine — cPanel’s own RPMs, its MySQL credentials, a masked unit, an unprivileged uid.

  • Accounts are registered in place, with their uids, their password hashes and their home directories exactly where they are.
  • Database users are adopted rather than re-created, because the MySQL being taken over is still cPanel’s, and so is its root login.
  • An account that landed short is repaired — the run re-examines it and creates only what is missing, instead of skipping it or starting again.
  • The DNS zones are published as the cutover’s ninth step, with the same records on the same port, and DNS is deferred the way the home directory mode is.
  • The home directories are tightened and the quotas applied inside the window, because doing either earlier would take live sites offline or suspend a live account mid-afternoon.
  • The cutover’s unit lists were corrected against a real cPanel server, and a masked web server is treated as an instruction rather than as a failed reload.
  • The server’s own address, hostname and nameservers are adopted from cPanel, which is why the panel opens on its dashboard rather than on the configuration assistant.
  • PREPARE gained its execution engine and its preflight (1.3.4), and the installer is driven from the install step rather than by hand.
  • The check’s verdict is colour-coded and agrees with its own numbers (1.3.2), and the build date is stamped into the binary.
  • plan gained a verdict, and names the domains that have no zone file (1.3.4).
  • Hosting packages and the limits accounts actually ran under are read, and an account is attached to its package rather than only to its numbers (1.3.0).
  • An account is imported as closed as the source left it — a suspended cPanel account arrives suspended (1.3.1).
  • The database version is read from the running server rather than from the stamp in the data directory (1.3.0).
  • Disabled FTP is no longer reported as a source the tool could not find (1.3.3).
  • The end of the check says what happens next instead of naming a command (1.3.5).

Before 1.3.0 — 28 July to 10 August 2026

Section titled “Before 1.3.0 — 28 July to 10 August 2026”

The tool started as the read-only migration check (0.5.0) and grew the plan command in the same release. What is worth recording from that period is mostly what was taken out: the report upload, because the tool is the conversion rather than a lead form, and an ELEVATE phase, because upgrading an operating system is cPanel’s own elevate-cpanel job and not this tool’s. The rest were scoring corrections — telling ImunifyAV apart from Imunify360, not charging a server for running JetBackup, warning when one account is split across PHP versions.