HestiaCP is the panel small hosts reach for when they want a free, NGINX + Apache stack with built-in mail and DNS — the legitimate successor to VestaCP after that project went quiet in 2019. It works. The migrations we see off Hestia are almost never about a failure of the panel itself: they're about a hosting business that needs reseller accounts, CageFS-level account isolation, a billing-platform-integrated control panel, JetBackup, LiteSpeed Enterprise, or just a brand customers already recognise. None of which Hestia ships.
This guide is the practical, account-by-account playbook for moving an existing HestiaCP 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 roughly 20-30 minutes of operator time per account plus 15-30 minutes of customer-visible downtime per DNS cutover. A 30-user Hestia box is a comfortable day of operator work spread across two evenings.
What doesn't translate one-to-one
HestiaCP and cPanel make different defaults that you need to plan around:
| HestiaCP | cPanel equivalent |
|---|---|
| NGINX (proxy) + Apache (backend) | Apache, or LiteSpeed Enterprise as a paid add-on |
| Exim + Dovecot + RoundCube + Sieve | Exim + Dovecot + Webmail (Roundcube/Webmail Lite) |
| NSD as authoritative DNS | BIND, PowerDNS, or DNS-only cluster |
hestia-php 7.4 – 8.3 multi-PHP | EasyApache 4 PHP versions per account |
Sites under /home/<user>/web/<domain>/public_html/ | Sites under /home/<user>/public_html/ per account |
Mail in /home/<user>/mail/<domain>/<acct>/ (Maildir) | Mail in /home/<user>/mail/<domain>/<acct>/ (Maildir) |
v-backup-user tarballs in /backup/ | pkgacct tarballs in /home/ |
| Single-server, no reseller | WHM reseller accounts and ACLs |
| Built-in fail2ban + iptables | cPHulk, ConfigServer Firewall, or Imunify360 |
The biggest structural difference is that Hestia accounts can hold multiple primary web
domains under one Linux user, while cPanel has one primary domain per account and treats
additional sites as addon domains under the same /home/<user>. If a Hestia user has three
unrelated customer sites bundled together, you have a decision to make at cutover: keep
them under one cPanel account (cheaper, less isolated) or split them into one cPanel
account each (more billable, properly isolated). Most hosts use the migration as the moment
to split.
Step 1 — Inventory the source
SSH into the Hestia server as root and pull a complete picture before touching anything:
# All Hestia users (excludes the admin and system reserved)
v-list-users plain | awk '$1 != "admin" {print $1}'
# Every domain on every user
for u in $(v-list-users plain | awk '{print $1}'); do
echo "=== $u ==="
v-list-web-domains $u plain
done
# Mail accounts
for u in $(v-list-users plain | awk '{print $1}'); do
for d in $(v-list-mail-domains $u plain | awk '{print $1}'); do
v-list-mail-accounts $u $d plain
done
done
# Databases and their owners
for u in $(v-list-users plain | awk '{print $1}'); do
v-list-databases $u plain
done
# DNS zones managed by NSD
v-list-dns-domains admin plain
Capture all of that into a single migration manifest spreadsheet. The columns you want are
Hestia user, web domains, database names, mail domains, DNS zones, and a notes column for
oddities. The oddities matter — a database with no matching domain usually means a deleted
site that never got cleaned up, and a domain pointing at a /web/ directory that doesn't
exist is a customer who pointed DNS but never built the site.
Step 2 — Prepare the cPanel destination
On the destination cPanel server:
-
Match the source's PHP versions through EasyApache 4. Hestia ships PHP 7.4 through 8.3; check which versions the source actually serves with
v-list-sys-php. Install the matching versions following adding a PHP version to EasyApache 4. A site running PHP 7.4 on Hestia will not silently work on PHP 8.2. -
Match MariaDB / MySQL. Hestia defaults to MariaDB on Debian/Ubuntu and MySQL on RHEL-family. Run
mysql --versionon the source to confirm, then match it on the cPanel destination — this avoids the JSON-column and authentication-plugin edge cases that bite cross-engine restores. -
Pick the web server now, not later. If your customer sites depend on NGINX-level caching configured by Hestia, the cleanest cPanel path is LiteSpeed Enterprise with LSCache for WordPress. Plain cPanel Apache is workable but slower. Install LSWS before the first account move so document roots come up under LiteSpeed from day one.
-
Decide your DNS topology before the cutover. If the Hestia server was authoritative via NSD, you can either keep its IP serving DNS only post-migration, or rebuild the zones on the new cPanel BIND. Most operators move DNS to a DNS-only cluster on the same migration weekend.
-
Pre-create resellers and packages. If you're splitting one Hestia user into multiple cPanel accounts, your WHM hosting packages (disk, bandwidth, addon-domain count) need to exist before account creation. Build them under WHM → Packages → Add a Package.
Step 3 — Package each Hestia user
Hestia's v-backup-user creates a tarball that contains the user's web roots, mail
maildirs, databases, DNS zones, and cron — everything you need for migration in one file.
The tarball is not in pkgacct format, so cPanel's transfer wizard won't ingest it, but
the contents extract cleanly with tar and the pieces map directly onto cPanel paths.
On the source for each user:
USER=customerone
v-backup-user $USER
ls -lh /backup/$USER.*.tar | tail -1
The backup lands at /backup/<user>.YYYY-MM-DD_HH-MM-SS.tar. Inside, the structure is:
backup/
├── conf/ # nginx, apache, exim, dovecot per-domain configs
├── web/ # all public_html trees per domain
├── mail/ # Maildir per mail account
├── dns/ # NSD zone files
├── db/ # mysqldump output per database
└── user.conf # quotas, package, contact email
Move the tarball to the destination via rsync rather than scp — rsync is resumable
on a flaky network and a single user's tarball can be tens of gigabytes on a mail-heavy
account:
rsync -P /backup/$USER.*.tar destination:/home/
Step 4 — Restore each user as a cPanel account
On the destination cPanel server, create the new account in WHM under Account Functions → Create a New Account. Pick the cPanel username carefully — Hestia usernames are often longer than cPanel's 16-character limit, so plan a shortened mapping in your migration spreadsheet and notify customers if their FTP username will change.
With the account created, restore the pieces:
CUSER=customer1 # cPanel username
HUSER=customerone # Hestia username
DOMAIN=example.com # primary domain on cPanel
TARBALL=/home/${HUSER}.*.tar
mkdir -p /tmp/hestia-restore-$CUSER
tar -xf $TARBALL -C /tmp/hestia-restore-$CUSER
# Web files — primary domain into public_html, addon domains into subfolders
rsync -av /tmp/hestia-restore-$CUSER/web/$DOMAIN/public_html/ \
/home/$CUSER/public_html/
chown -R $CUSER:$CUSER /home/$CUSER/public_html
# Databases — Hestia prefixes with username, so rename on restore
for sql in /tmp/hestia-restore-$CUSER/db/*.sql; do
oldname=$(basename $sql .sql)
newname=$(echo $oldname | sed "s/^${HUSER}_/${CUSER}_/")
mysql -e "CREATE DATABASE \`$newname\`"
mysql $newname < $sql
done
# Mail — Maildirs preserve folder structure
for maildomain in /tmp/hestia-restore-$CUSER/mail/*/; do
d=$(basename $maildomain)
mkdir -p /home/$CUSER/mail/$d
rsync -av $maildomain/ /home/$CUSER/mail/$d/
done
chown -R $CUSER:$CUSER /home/$CUSER/mail
The database rename matters: cPanel enforces a <cpaneluser>_<dbname> prefix and rejects
operations on databases that don't match it. Restoring under the old Hestia prefix leaves
the database visible only over SSH, never in cPanel's MySQL UI.
After the file restore, update any WordPress wp-config.php, Joomla configuration.php,
and Drupal settings.php to use the new database name and the new MySQL password cPanel
generated:
sed -i "s/${HUSER}_/${CUSER}_/g" /home/$CUSER/public_html/wp-config.php
sed -i "s/DB_PASSWORD',.*'/DB_PASSWORD', 'newpass-here'/" /home/$CUSER/public_html/wp-config.php
The site-config edit is the step people skip and then waste an hour debugging "internal server error" on a freshly migrated site.
Step 5 — Recreate mail accounts and DKIM
The Maildir data is already in place, but cPanel needs the 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.
- Either set the same password the customer was using (if you captured it during a pre-migration prompt) or force a reset and notify them.
- Re-publish DKIM. Hestia generates DKIM keys per domain under
/etc/exim4/domains/<domain>/dkim.pem; those keys are not portable to cPanel. 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 at cutover.
Reusing the old DKIM selector tanks deliverability for a week while inbox providers learn the new key. Publish the new selector ahead of the DNS cutover so the records are warm when mail starts flowing.
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-migration AutoSSL failures are stale CAA records published by Hestia's Let's Encrypt integration.
Step 7 — Decommission carefully
Leave the Hestia 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 — IMAP clients can fail over slowly, and any mail delivered to the old Maildir 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. Reusing the Hestia OS image to spin up new servers is fine — Hestia itself isn't the security liability that drives most CyberPanel migrations.
FAQ
Is there a one-shot tool to migrate HestiaCP to cPanel?+
Can I keep one Hestia user's multiple sites under one cPanel account?+
Will customer mailbox passwords survive the migration?+
What happens to DKIM keys when I move from HestiaCP to cPanel?+
How long does a HestiaCP to cPanel migration take?+
Should I migrate to Plesk instead of cPanel?+
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 Hestia's NGINX 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 licence — running migration loads against the trial activation hits rate limits at the wrong moment.