CentOS Web Panel — now branded Control Web Panel since the CentOS rebrand, but everyone
still types cwp into the URL bar — is the free panel that most small RHEL-family hosts
land on after outgrowing a manual LAMP build. It runs Apache (with optional NGINX reverse
proxy or Varnish), Postfix + Dovecot for mail, and BIND for DNS, all wrapped in a PHP UI
that listens on ports 2030/2031 (free) and 2086/2087 (Pro). It's free, it works, and the
moment you need reseller accounts that you can actually bill, CageFS-grade tenant isolation,
JetBackup-quality backup, or a panel your customers have heard of, you start planning the
move.
This guide is the practical, account-by-account playbook for moving an existing CWP server to cPanel & WHM. It assumes a working cPanel destination — if you don't have one yet, start with installing cPanel on a VPS. Budget 25-35 minutes of operator time per account plus 15-30 minutes of customer-visible downtime per DNS cutover. A 25-user CWP box is a comfortable day of operator work split across two evenings, including the mail format conversion that's the slowest part.
What doesn't translate one-to-one
CWP and cPanel make different defaults that you need to plan around:
| CWP | cPanel equivalent |
|---|---|
| Apache + optional NGINX/Varnish front | Apache, or LiteSpeed Enterprise as a paid add-on |
| Postfix + Dovecot + Roundcube | Exim + Dovecot + Webmail (Roundcube/Webmail Lite) |
| BIND, free-form zone files | BIND or PowerDNS, or a DNS-only cluster |
cwpsrv-php 7.4 – 8.3 multi-PHP | EasyApache 4 PHP versions per account |
Sites at /home/<user>/public_html/ | Sites at /home/<user>/public_html/ |
Mail at /home/vmail/<domain>/<acct>/Maildir/ | Mail at /home/<user>/mail/<domain>/<acct>/ |
| Databases with no enforced prefix | Databases enforced to <cpaneluser>_<dbname> |
CWP backup .tar.gz under /home/cwp-backup/ | pkgacct tarballs under /home/ |
| Built-in CSF + ModSecurity | cPHulk + ConfigServer Firewall or Imunify360 |
| Single-server, no real reseller tier in Free | WHM resellers with ACLs and packages |
The two structural traps to plan for are mail layout and database naming. CWP runs
Postfix with vhost-style mail storage under /home/vmail/, a directory owned by the
vmail user and unrelated to the customer's Linux account. cPanel runs Exim with mail
under each customer's own /home/<user>/mail/ tree. The Maildir contents themselves are
byte-compatible — Dovecot wrote them the same way on both panels — but the path move and
ownership rewrite are mandatory or IMAP won't serve a single folder.
Database naming is the second trap. CWP lets a user create wp_main, crm, and
shop_db_2023 with no prefix. cPanel enforces <cpaneluser>_<dbname> and refuses to
manage databases that don't match. Plan a rename map in your migration manifest before you
restore anything.
Step 1 — Inventory the source
SSH into the CWP server as root and pull a complete picture before touching anything.
CWP keeps its account metadata in MySQL in the root_cwp database, which is the fastest
place to list everything:
# All CWP accounts (excludes the cwp admin)
mysql -e "SELECT username, domain, package, owner, status FROM root_cwp.user;"
# Addon and subdomains per primary account
mysql -e "SELECT username, parked, type FROM root_cwp.domains;"
# Mail accounts (Postfix virtual mailbox table)
mysql -e "SELECT id, email, quota FROM root_cwp.email_accounts;"
# Databases and their owners
mysql -e "SELECT username, db_name FROM root_cwp.mysql_db;"
# DNS zones managed by BIND
ls /var/named/*.db | awk -F'/' '{print $4}' | sed 's/\.db$//'
Dump the four queries into a spreadsheet and add columns for the target cPanel username
(remember the 16-character limit), the renamed database name, and a notes column for
oddities. The notes are where you catch the customer who has 12 GB of mail in a single
Maildir and the one whose public_html is actually a symlink to /var/www/.
Step 2 — Prepare the cPanel destination
On the destination cPanel server:
-
Match the source's PHP versions through EasyApache 4. CWP exposes PHP 7.4 through 8.3 via cwpsrv-php; check which versions the source actually serves in CWP → PHP Selector → PHP Settings per User, then install matching versions per the EasyApache 4 add-PHP-version guide. A site running on PHP 7.4 with deprecated extensions will not silently work on PHP 8.2.
-
Match MariaDB / MySQL. CWP defaults to MariaDB 10.5 or 10.11 depending on install age. Run
mysql --versionon the source, then match it on the destination — this avoids the JSON-column andmysql_native_passwordedge cases that bite cross-version restores. -
Pick the web server and security stack before the first cutover. If the CWP server was running NGINX in front of Apache for cache, the cleanest cPanel equivalent is LiteSpeed Enterprise with LSCache for WordPress. For the security replacement, install Imunify360 before the first account move so ModSecurity rules and proactive defence are live from day one.
-
Decide your DNS topology before the cutover. CWP runs BIND with zone files under
/var/named/, which copy cleanly to cPanel's BIND. Most operators take the migration weekend to move DNS to a DNS-only cluster and stop running authoritative DNS on the same box as the web stack. -
Pre-create resellers and packages. Build your WHM hosting packages (disk, bandwidth, addon-domain count, feature lists) under WHM → Packages → Add a Package before you start creating accounts. CWP's free tier doesn't have a real reseller layer, so this is often the first time those packages get defined cleanly.
Step 3 — Package each CWP user
CWP's built-in User Backups tool creates a per-account .tar.gz containing web roots,
databases, mail, DNS zones, FTP accounts, and the user's cron — everything you need in one
file. It's not in pkgacct format (so cPanel's transfer wizard can't ingest it directly),
but the contents extract cleanly with tar and the pieces map onto cPanel paths.
On the source, generate a fresh backup for each user via the CWP CLI:
USER=customerone
/scripts/cwp_backup_user $USER
ls -lh /home/cwp-backup/${USER}-*.tar.gz | tail -1
The backup lands at /home/cwp-backup/<user>-YYYY-MM-DD.tar.gz. Inside, the structure is:
home/<user>/
├── public_html/ # primary domain web root
├── public_html_addon/ # addon and subdomains (if any)
mysql/ # per-database mysqldump output
mail/ # Maildir trees pulled out of /home/vmail
dns/ # BIND zone files
account.info # quotas, package, contact email, plain-password (Free) or salted (Pro)
Move the tarball to the destination via rsync — it's resumable and a mail-heavy account
can easily be 30+ GB:
rsync -P /home/cwp-backup/${USER}-*.tar.gz destination:/home/
Step 4 — Restore each user as a cPanel account
On the destination, create the new account in WHM → Account Functions → Create a New Account. Pick the cPanel username carefully — CWP usernames are often longer than cPanel's 16-character limit, so plan a shortened mapping in your manifest and notify customers if their FTP username will change.
With the account created, restore the pieces:
CUSER=customer1 # cPanel username
HUSER=customerone # CWP username
TARBALL=/home/${HUSER}-*.tar.gz
mkdir -p /tmp/cwp-restore-$CUSER
tar -xzf $TARBALL -C /tmp/cwp-restore-$CUSER
# Web files — primary domain into public_html
rsync -av /tmp/cwp-restore-$CUSER/home/$HUSER/public_html/ \
/home/$CUSER/public_html/
chown -R $CUSER:$CUSER /home/$CUSER/public_html
# Databases — CWP has no enforced prefix, cPanel requires <cuser>_
for sql in /tmp/cwp-restore-$CUSER/mysql/*.sql; do
oldname=$(basename $sql .sql)
newname="${CUSER}_${oldname}"
mysql -e "CREATE DATABASE \`$newname\`"
mysql $newname < $sql
done
# Mail — convert /home/vmail layout to cPanel /home/<user>/mail
for maildomain in /tmp/cwp-restore-$CUSER/mail/*/; do
d=$(basename $maildomain)
for acct in $maildomain/*/; do
local_part=$(basename $acct)
mkdir -p /home/$CUSER/mail/$d/$local_part
rsync -av $acct/Maildir/ /home/$CUSER/mail/$d/$local_part/
done
done
chown -R $CUSER:$CUSER /home/$CUSER/mail
The database rename is the step people skip and then waste an hour debugging "this
database is not visible in cPanel". cPanel enforces the <cpaneluser>_ prefix and rejects
operations on databases that don't match.
After the file restore, update any wp-config.php, configuration.php, or settings.php
to point at the renamed databases and the new MySQL passwords cPanel generated:
sed -i "s/'DB_NAME', *'/'DB_NAME', '${CUSER}_/" /home/$CUSER/public_html/wp-config.php
sed -i "s/DB_PASSWORD',.*'/DB_PASSWORD', 'newpass-here'/" /home/$CUSER/public_html/wp-config.php
Step 5 — Recreate mail accounts and DKIM
The Maildir data is now in place on disk, but cPanel needs each mailbox to exist in its own account database before IMAP will serve it. For each mail account on the source:
- Create the account in cPanel → Email Accounts with the same local part.
- Set the same password the customer was using (CWP Pro stores these salted, CWP Free
keeps them retrievable from
root_cwp.email_accounts), or force a reset and notify them in advance. - Re-publish DKIM. CWP generates DKIM keys per domain under
/etc/opendkim/keys/<domain>/and those keys are not portable to cPanel's Exim-based DKIM. Generate fresh DKIM in cPanel → Email Deliverability for each domain, and follow cPanel email deliverability with SPF, DKIM, and DMARC to get the records right before the DNS cutover.
The Postfix-to-Exim mail server transition is the most visible change for customers. Folder
structure and read/unread flags carry over from the Maildir copy, but anything stored in
Sieve filters on CWP needs to be reconfigured in cPanel's webmail filters — Sieve scripts
under /home/vmail/<domain>/<acct>/sieve/ are syntactically compatible but the path and
delivery agent differ enough that copying them in raw rarely works.
Step 6 — DNS cutover
Drop public DNS TTLs to 300 seconds at least 24 hours before the cutover. On the day:
- Verify the destination cPanel site responds correctly over the new IP:
curl -I --resolve example.com:443:NEW.IP.HERE https://example.com/. A 200 or expected redirect means the document root and SSL are wired up. - Update A and AAAA records at the registrar or external DNS to point at the new server.
- If you're moving DNS hosting too, change the NS records last and only after the authoritative zones serve correctly from the new nameservers.
- Trigger AutoSSL on the new account from WHM → SSL/TLS → Manage AutoSSL → Check "username" Now. If issuance fails, work through the WHM AutoSSL failures decision tree — most post-CWP AutoSSL failures are stale CAA records published by CWP's certbot integration that pin issuance to Let's Encrypt and exclude the Sectigo CA that cPanel AutoSSL defaults to.
Step 7 — Decommission carefully
Leave the CWP server up for at least 7 days post-cutover, serving the same vhosts so
direct-IP traffic and stale resolvers continue to land somewhere. Pull mail from the old
IMAP store once per day during the grace window — any mail delivered to the old /home/vmail/
tree in that window is invisible to the customer until you copy it across.
After the grace period, snapshot the source, archive it to cold storage for 90 days (enough to cover a customer "I lost a file" ticket), and destroy the instance. Unlike CyberPanel — where there's a clear security argument for never reusing the OS image — a patched CWP box is a clean enough Linux install to repurpose if you want, though most operators take the migration as the moment to standardise on a fresh AlmaLinux 9 build for the next server too.
FAQ
Can I use cPanel's transfer wizard to import a CWP account?+
Does CWP's free-tier license cause migration problems?+
How do I convert Postfix mail storage to cPanel's Exim layout?+
Will customer mailbox passwords survive the migration?+
What happens to DKIM keys when I move from CWP to cPanel?+
How long does a CWP to cPanel migration take?+
Next steps
- New to cPanel? Walk through the cPanel VPS install, then WHM two-factor authentication setup before exposing WHM to the internet.
- Want LiteSpeed performance on the destination to match CWP's NGINX-fronted Apache layer? See installing LiteSpeed on cPanel and enabling LSCache for WordPress on cPanel.
- Activate your destination panel before the first cutover with a fresh cPanel license — running migration loads against the trial activation hits rate limits at exactly the wrong moment.