# The cPanel migration check

> Run one read-only command on a cPanel server to see what CorePanel would take over: accounts, domains, mail, databases, DNS and SSL, with a score and the exact list of things that need attention. It installs nothing and changes nothing.

Source: https://www.corepanel.net/docs/migration-check/
Last updated: 2026-09-03
Part of the CorePanel documentation — https://www.corepanel.net/docs

---

Before moving anything, you can find out what a move would involve. The **migration
check** is a single command you run on your **cPanel server**. It reads that server's
configuration and prints an inventory, a score, and the specific list of things that
would need attention — then deletes itself.

```bash
curl -fsSL https://get.corepanel.net/check | sh
```

It must run as **root**, because the things worth reporting on — accounts, mailbox
hashes, per-domain PHP versions — live in files only root can read.

> **It reads, and that is all**
>
> The check opens files. It does not install anything, does not start or stop a service,
> and does not change a configuration file. There is no account to create and no email to
> give. The report stays in your terminal, and the last line it prints is *"Nothing on this
> server was modified."*
> **One thing does leave the server**
>
> When the check finishes it sends us an anonymous summary of what it found: the versions
> above, the counts, and the codes of the findings — never a hostname, a domain, an account
> name or an address. It is how we learn which blockers actually stop people, and it is the
> only reason this page can tell you what the common ones are.
>
> It is on by default and off with one flag:
>
> ```bash
> curl -fsSL https://get.corepanel.net/check | sh -s -- --no-telemetry
> ```
>
> `COREPANEL_NO_TELEMETRY=1` in the environment does the same, permanently. The check tells
> you in its own last lines which of the two happened, and the exact field list is in
> [Anonymous usage statistics](https://www.corepanel.net/docs/telemetry#the-migration-tools-report).
## What it tells you

```
CorePanel migration check
=========================

Server:   cp.example.com
OS:       AlmaLinux 9.8
cPanel:   11.136.0.35
Database: 10.11.19-MariaDB
PHP:      ea-php81, ea-php82
DNS:      powerdns
FTP:      pure-ftpd

Inventory: 5 accounts, 11 domains, 4 mailboxes, 3 databases, 8 DNS zones, 9 certificates
Packages:  2 (default, starter)

Score: 100/100  —  ready to transform

NOTES (3)
  • JetBackup is installed on this server
    → account backups carry over as a capability: CorePanel has its own backup system…

────────────────────────────────────────────────────────────────────
  ✔  READY TO TRANSFORM
     score 100/100 · nothing on this server stands in the way
────────────────────────────────────────────────────────────────────

WHAT COMES NEXT

  PLAN     the ordered list of steps, with how long each service is offline.
           Read-only, like this check.

  PREPARE  installs CorePanel beside cPanel, with nothing going offline:
           cPanel keeps serving every site throughout.

  CUTOVER  is the only offline window — seconds rather than hours, because
           nothing is copied.

  VERIFY   checks that every domain, mailbox, database and zone answers.

  'rollback' gives the server back to cPanel at any point until 'cleanup'.

Nothing on this server was modified.
```

Add `-v` to list every account individually, with its domains, mailboxes, databases,
cron jobs, certificates and its own findings:

```bash
curl -fsSL https://get.corepanel.net/check | sh -s -- -v
```

## The verdict

The last thing the check prints is its answer, in one of three states:

| | Banner | What it means |
|---|---|---|
| 🟢 | **READY TO TRANSFORM** | Nothing on this server stands in the way |
| 🟡 | **READY TO TRANSFORM, WITH ITEMS TO REVIEW** | It works, but some items degrade or need manual work — they are listed above the banner |
| 🔴 | **CANNOT BE TRANSFORMED IN PLACE** | One or more blockers have to be resolved first. The banner deliberately offers no next command |

The same answer appears next to the score at the top of the report; it is repeated at
the end because on a server with dozens of findings the top has long scrolled away.

Colour is a convenience of the terminal, not part of the report: it is used only when
the output is a terminal, so a run redirected into a file or pasted into a ticket is
plain text. `NO_COLOR` turns it off, and `--color=always` forces it on.

## Reading the findings

Every finding carries one of three severities, and they mean genuinely different things.

| | Meaning | What to do |
|---|---|---|
| **Blocker** | This server cannot be migrated as it stands | Each blocker names its way out. The common one is the operating system: CorePanel runs on RHEL 8–10 and its rebuilds |
| **Warning** | The migration works, but something specific degrades | Read it before you schedule anything. Most warnings are per-domain and name the file and the line |
| **Note** | Reported for completeness, costs no score | Nothing to do. A note is there so nothing arrives as a surprise later |

### Handled by the cutover

Below the findings, the check prints a section of its own: **HANDLED BY THE
CUTOVER**. Your server is probably not just cPanel — it carries a WAF, a
firewall, a backup product, maybe a proxy in front of Apache — and those are not
problems to solve. CorePanel brings what each of them was doing, and the
transformation stops them for you.

| Product | What takes over |
|---|---|
| NGINX (cPanel's NGINX Manager), Engintron, Varnish, LiteSpeed | corehttpd, which serves every site and caches pages itself |
| Imunify360 / ImunifyAV, BitNinja, cPGuard | CorePanel's WAF — included in **every** edition — and Pyxsoft Anti Malware for scanning |
| Bought ModSecurity rule sets (Comodo, Atomicorp, …) | the same WAF. There is nothing to buy and nothing to install |
| JetBackup | CorePanel's own backups. Existing archives are not adopted — keep them reachable until the rollback window closes |
| Softaculous | CorePanel's application installer. Installed applications keep working: they are just files |
| CSF/LFD, or PyxSoft's `pxf` | corepanel-auth takes over brute-force protection. The firewall itself is turned off rather than kept — see below |

Nothing on that list is uninstalled, and nothing on it needs doing before you
start. Each is stopped, disabled and — for the ones that restart themselves —
masked, so a rollback puts every one of them back.

### The host firewall is turned off

CSF/LFD, or PyxSoft's `pxf`, is disabled during the cutover — with `csf -x` and
`pxf disable`, their own commands, because stopping the unit leaves every rule
in the kernel. This is not offered as a choice, for two reasons: it opens
cPanel's ports and not CorePanel's, so a server that keeps it is a server whose
passive FTP does not work; and ConfigServer closed in August 2025, so CSF has no
upstream — what is on the machine is unmaintained software configured for a
panel that is about to be gone.

**That leaves the server without a firewall until you put one back.** The check
and the cutover both say so in those words. firewalld is the one CorePanel
manages, from the panel or the CLI, and it is what should be in front of the
server before it is left to itself. If you roll the transformation back, `csf -e`
and `pxf enable` are the exact reverse and `rollback` runs them for you.

It has one consequence that is worth knowing about, because nothing on the
server announces it: on a **dual-stack** machine, taking the firewall down can
change *which of the server's two addresses its outbound connections leave
from*. With no policy in `/etc/gai.conf`, the C library prefers IPv6 to IPv4 for
every destination that offers both — so a server that had been seen at its IPv4
for years starts being seen at its IPv6, and everything licensed against the
address (WHMCS, Softaculous, LiteSpeed, JetBackup) starts failing its licence
check. Not at the cutover: each product runs on a cached verdict for hours, so
the first symptom is a licence error in the middle of the night on a server whose
migration went perfectly. The cutover pins the preference back to IPv4 for you,
right after the firewall goes down. If you have written your own policy into
`/etc/gai.conf` the run leaves it alone and tells you to re-check those licences
yourself.

### Mail your applications send stays working

cPanel's Exim offers authentication on port 587 without requiring TLS first, and CorePanel
installs Postfix the other way round, because requiring it is the right default for a
server nobody has migrated anything onto. That difference is invisible until it isn't: an
application on the server configured to send mail unencrypted to its own host — a billing
system, a WordPress plugin, a shop — simply stops sending, with an authentication error
that has nothing to do with its credentials.

The cutover reads what your mail server advertises **before** it stops it, and configures
Postfix to match. A server whose Exim allowed it keeps allowing it; one that already
required TLS stays strict. Either way STARTTLS is offered, so mail clients go on
encrypting, and a CorePanel server installed from scratch is unaffected.

It is a compatibility setting, not a target state. Once those applications are moved onto
STARTTLS, this puts the strict default back:

```bash
postconf -P 'submission/inet/smtpd_tls_security_level=encrypt'
systemctl reload postfix
```

### The short list that does need you

Three things the transformation deliberately does **not** decide for you, because
each would cost something silently:

- **MailScanner** holds mail in a spool of its own, and the cutover flushes
  Exim's queue rather than that one. Let it drain and stop it *before* the
  window.
- **MailChannels** relays your outbound mail by configuring Exim. Postfix takes
  the mail at the cutover, so from that moment the server sends directly from
  its own address — check your SPF and your reputation first.
- **A block-level backup agent** (R1Soft, Acronis) keeps running, but its policy
  names cPanel's paths. Repoint it, or the first backup after the cutover
  silently protects nothing.

### A site that is already broken

The check asks each of this server's own sites for its homepage, the way a visitor would,
and tells you about the ones that are **not working right now, before anything has been
transformed**:

```
  • billing.example.com is not working right now, before anything has been
    transformed: the ionCube loader is missing…
    → fix it before the cutover or accept that it stays broken afterwards
```

It is not scored. Nothing about a customer's broken site makes this server harder to move,
and the transformation neither causes it nor repairs it. It is reported because of what
happens if nobody says it out loud: the site is still broken afterwards, the health gate at
the end of the cutover finds it, and it reads as a fresh failure — over a page that was an
error message before the run started.

Two kinds turn up:

- **A page that announces its own failure with HTTP 200.** Encoded software — WHMCS and
  most billing systems — prints "the ionCube Loader needs to be installed" and stops, and
  PHP calls that a successful request. Nothing about the status code shows it.
- **A homepage that is a PHP error and nothing else.** cPanel runs suPHP with
  `display_errors` on, so the error *is* the page and it is served as a 200. CorePanel runs
  PHP-FPM with it off, so the same fault becomes an honest 500. The site is no worse
  afterwards — it stops pretending to work, which is worth knowing before you see the 500
  and reach for the rollback.

`--no-dns` turns this pass off along with the domain lookups, for when you are scanning a
copy of a server rather than the live one.

### CloudLinux

CloudLinux is a blocker, and the check gives you the removal command. It is not
a capability problem: the limit people buy LVE for is the entry process, and
that is what corehttpd does per account — with the same `508` — alongside
per-request memory and time ceilings and a global one that protects the box.
See [PHP Isolation and Limits](https://www.corepanel.net/docs/php/isolation-and-limits).

It has to come off first for two reasons that have nothing to do with what
CorePanel can host: removing it swaps the kernel and needs a reboot, so it
cannot happen inside the cutover window; and CageFS shows each user a different
filesystem, which makes every measurement taken under it a measurement of a
server nobody serves.

Run CloudLinux's own procedure while cPanel is still installed, then run the
check again. It moves your sites off `alt-php`, so check each domain's PHP
version afterwards — and on CloudLinux 7 you will land on CentOS 7, which needs
one more step: cPanel's elevate, as far as AlmaLinux 9.

The **score** is the weighted result of those findings. It is not a marketing number: it
is computed from a versioned rule set built into the binary, so it always reflects what
the current CorePanel actually supports rather than what a brochure claims.

### Databases

The check reports which database engine the server runs and what a transformation
would do with it. There are three answers, and which one applies is decided by the
engine itself:

| Engine | What happens | Cost |
|---|---|---|
| MariaDB **10.11 or newer** | adopted exactly where it is — same data directory, same server, same version | seconds, whatever the data size |
| MariaDB **older than 10.11** | upgraded in place with `mariadb-upgrade` over the same data directory | minutes; nothing is copied |
| **MySQL** (any version) | dumped and restored onto MariaDB | the only step whose downtime grows with the amount of data |

**10.11 is a minimum, not a target.** CorePanel keeps its own data in SQLite and
never uses MySQL — the database on a hosting server belongs to the sites. So a
server already running MariaDB 11.4 or 11.8 is left on it: nothing is reinstalled,
no repository is swapped, and no downgrade is ever performed. You get a note
saying so, which costs no score, and the server transforms like any other.

The one direction that does not exist is backwards. MariaDB upgrades in place but
never downgrades, and nothing in a transformation asks it to.

### Resellers

A server whose accounts belong to resellers gets a note saying so, naming each reseller
and how many accounts it owns. This is not a warning, because nothing is lost: cPanel
resolves an account's direct owner and stops, so one level *is* the source, and each
reseller becomes a CorePanel **seller** owning the same accounts.

Two things are worth knowing before the transformation runs:

- The seller is created from the reseller's name alone. It arrives with **no contact
  email and no login** — the reseller's own contact details live in their own account's
  metadata, which is not the migrating account's to read. Give the seller both once the
  transformation is done.
- If a reseller is itself owned by another reseller, a second note says the ownership is
  read as one level. cPanel allows that shape but does not treat it as a hierarchy
  anywhere, so nothing is being simplified away.

### The billing system that provisions this server

If a WHMCS has been provisioning this server over the WHM API, the check reports the
credentials it finds under **CARRIED OVER** — and carried over is exactly what happens
to them.

cPanel stores a WHM API token as its SHA-512 and never the token itself: the plaintext
was shown once, at creation, and the only party that still holds it is the billing
system sending it in every request. That digest is all CorePanel needs to accept the
same token, so the transformation carries it across and **nothing changes on the WHMCS
side** — no new token, no server entry to edit, and nobody here ever learns it.

The old access hash is carried too, but only where the server still honours it. cPanel
turned it off by default; where `allow_deprecated_accesshash` is off, WHM answers
*Access denied* to it, and importing it would mean CorePanel accepting a credential the
old server refused.

The WHM API itself stays **closed**. Port 2087 is the most attacked port in shared
hosting, so nothing opens it as a side effect — not the installer, not the
transformation. When you are ready:

```bash
corepanel whmcompat enable     # serve the WHM API on :2087 (Business)
corepanel whmcompat selftest   # drive a real WHM API request and report what came back
```

### More websites than the free edition allows

CorePanel Personal — the free edition — hosts **20 websites**. Primary and addon
domains count; subdomains, parked names and aliases do not. If the check finds more
than 20 it says so, and it says so here rather than as a blocker, because the way out
is free and takes one command.

It matters that you see it before you start. The import registers accounts until the
cap and refuses every one after it with *"website limit reached for this edition"* —
which happens during PREPARE, with cPanel still serving and nothing lost, but it stops
the transformation halfway through an evening you set aside for it.

Pro and Business have no website cap, and a 14-day trial of either is free: no card,
no key. Once PREPARE has installed CorePanel and before the import runs:

```bash
corepanel license trial --edition business --email you@example.com
```

The most useful thing the check does is the **`.htaccess` audit**. CorePanel does not run
Apache, so every `.htaccess` on the server is read and classified: rules that are compiled
and applied (the WordPress and standard-CMS shapes, redirects, access rules, Basic auth),
rules that are deliberately not applied and are panel settings in CorePanel instead
(`Header`, `ErrorDocument`, `DirectoryIndex`, `SetEnv`, the `Expires*` family), and the
handful that genuinely need a human — `mod_security`, `RewriteMap`, `SetHandler`,
`Action`, `SetEnvIf`, SSI. Only the last group costs score.

## Keeping the tool

The one-liner downloads the binary, runs it and removes it — that is what "leaves
nothing behind" means, and it applies to the tool itself. To go further than the
check, ask the same one-liner to keep it:

```bash
curl -fsSL https://get.corepanel.net/check | sh -s -- --install
```

That installs `/usr/local/bin/corepanel-transform` from the copy it has already
downloaded and verified. `--keep` does the same thing *and* prints the check first.

## Running the transformation

Run the installed tool with no arguments and it walks the whole transformation as
one guided sitting, stopping to ask before every stage:

```bash
corepanel-transform
```

| | Stage | |
|---|---|---|
| 1 | **CHECK** | reads this server and says whether it can be transformed — read-only |
| 2 | **PLAN** | writes the ordered steps and the offline window — read-only |
| 3 | **PREPARE** | installs CorePanel beside cPanel; nothing goes offline |
| 4 | **CUTOVER** | the offline window: cPanel stops, CorePanel takes over |
| 5 | **VERIFY** | proves every domain, mailbox, database and zone answers |

Before each stage it prints what that stage does to *this* server — what goes
offline and for roughly how long, the steps it runs, whether it can be reversed —
and then asks. **Enter** goes on, **q** stops.

Stopping costs nothing. Every step records its outcome before the next one starts,
so `corepanel-transform` again picks up exactly where it stopped, and a stage
already finished is skipped rather than offered again. That is also what survives a
dropped SSH session or a reboot.

Two stages still ask for a word to be typed, after the prompt and before anything
in them runs: `I-HAVE-A-BACKUP` for PREPARE and `TRANSFORM` for CUTOVER.

The guided flow never offers to remove cPanel. cPanel stays installed and disabled
after VERIFY — that is what keeps `corepanel-transform rollback` available — and
removing it is `corepanel-transform cleanup`, a separate command for another day.

If you would rather drive it yourself, every stage is its own subcommand:
`check`, `plan`, `run --phase prepare|cutover|verify`, `resume`, `status`,
`rollback`, `cleanup`. The guided form refuses to run without a terminal and names
those instead, so a pipeline gets the subcommands rather than a wizard answering
its own questions.

## Keeping the report

By default nothing is written. To keep a copy — to read later, or to send to us with a
question — ask the binary for the file:

```bash
corepanel-transform check --report /root/migration-report.json
```

`report.json` is written owner-only and carries counts, finding codes and domain names.
It contains **no password hashes, no keys, no secrets and no file contents** — by
construction, and there is a test in the build that fails if that ever stops being true.

## Reporting, and turning it off

Every command of the tool — the check, and each stage of a transformation — sends an
anonymous summary of what it did. `--no-telemetry` on any of them, or
`COREPANEL_NO_TELEMETRY=1` in the environment, stops it:

```bash
corepanel-transform check --no-telemetry
corepanel-transform --no-telemetry            # the whole guided transformation
COREPANEL_NO_TELEMETRY=1 corepanel-transform  # the same, for a script
```

What is sent, in full, is on the [Anonymous usage statistics](https://www.corepanel.net/docs/telemetry#the-migration-tools-report)
page. The short version: versions, counts, finding codes, which stage ran, how long it
took and which step stopped it if one did — and no hostname, domain, account name or
error message, because there is no field for any of them.

## Exit status

The check answers with its status too, so it can be run from a script:

| | |
|---|---|
| `0` | The server can be transformed |
| `2` | It carries a blocker; the report names it and its way out |
| `1` | The check itself could not run |

## Verifying what you are about to run

The shim you pipe into `sh` carries the SHA-256 of each published binary and checks the
download against it before running anything; a mismatch aborts without executing the
file. If you would rather verify by hand, read the script first — it is short, and this
is exactly the kind of thing worth reading before running it as root:

```bash
curl -fsSL https://get.corepanel.net/check | less
```

## What comes after the check

Today the check answers one question: *what would moving this server involve?*

For **moving accounts onto a CorePanel server you already have**, the answer is the
[cPanel importer](https://www.corepanel.net/docs/cpanel-import): it pulls accounts over SSH from the live WHM, or
takes a staged `cpmove` archive, and recreates them natively while preserving passwords.
That is available now, and the check's inventory is a good way to see what it would find.

**Transforming a cPanel server into a CorePanel server in place** — same machine, same
IPs, no data copied, seconds of downtime instead of hours — is in development. The check
is the first part of it and is useful on its own: the score, the inventory and the
findings are the same ones that work will act on.
