The cPanel to Plesk migration guide you need actually exists — but most versions skip the hardest part: the settings Plesk Migrator deliberately does not transfer. If you are a web hosting provider or VPS owner reacting to cPanel's 2021–2025 licensing cost increases, this guide walks you through every step from pre-flight checks to production cutover, and lists every manual fix you must apply after the automated tool finishes.
Why Sysadmins Are Switching from cPanel to Plesk in 2025 (License Cost Reality Check)
In 2019 cPanel moved from a flat perpetual licence to per-account monthly pricing. By 2025 a server with 400 accounts costs upwards of $450/month — over $5,000 per year — just for the control panel. Plesk Web Pro covers unlimited domains for roughly $15–$25/month depending on tier.
Beyond cost, Plesk offers native Docker and Git integration, a cleaner REST API, and built-in Node.js/Python hosting that cPanel still gates behind CloudLinux add-ons. The switch makes financial and technical sense, but the migration itself carries real risk: emails, databases, DNS zones, and SSL certificates must survive the transition intact.
The official Plesk Migrator extension handles the heavy lifting — but it explicitly does not migrate Fail2Ban, ModSecurity rulesets, firewall rules, PHP handler configurations, or SpamAssassin's Bayes database. Every competing guide ignores this. This one does not.
Pre-Migration Checklist: Disk Space, SSH Access, Firewall Rules
Before you install anything, validate these requirements on both servers:
- Disk space: The Plesk destination server must have at least as much free space as the total data on cPanel, plus 5 GB overhead. Plesk Migrator writes a full copy before it removes anything.
- SSH key auth to cPanel server: Plesk Migrator connects as root via SSH (port 22 by default). Confirm password-based SSH is enabled or pre-share an SSH key.
- Plesk firewall ports open: TCP 22, 80, 443, 8443 (Plesk panel), 8880 (HTTP panel redirect), 21, 25, 110, 143, 465, 587, 993, 995.
- PHP versions matched: Note every PHP version in use on cPanel (check
php -vandcat /etc/cpanel/ea4/ea4.conf). Install matching PHP versions on Plesk before migration. - Lower DNS TTL now: 48–72 hours before cutover, reduce all zone TTLs to 300 seconds so DNS propagates in 5 minutes when you flip.
- Snapshot current Fail2Ban jails: Run
fail2ban-client statuson cPanel and save the output. You will need to recreate these in Plesk manually. - Export ModSecurity ruleset: Run
grep -r "Include\|SecRule" /etc/apache2/conf.d/modsec/and save all custom rules. - Document firewall rules: Run
iptables-save > /root/cpanel_iptables_backup.ruleson the source server.
Test SSH from the Plesk server to the cPanel server before proceeding:
ssh -p 22 root@<cpanel-server-ip> "echo SSH_OK"
Installing and Configuring the Plesk Migrator Extension
Plesk Migrator ships as a bundled extension in Plesk Obsidian and Plesk 18.x. Install or verify it via the command line:
# Check if already installed
plesk ext migrator --list
# Install if missing
plesk ext migrator --install
Alternatively from the GUI: Extensions > Extensions Catalog > search "Migrator" > Install.
Once installed, navigate to Extensions > Plesk Migrator > Start a New Migration. You will see a form asking for:
- Source panel type: cPanel
- Source server IP (the cPanel server's public IP)
- SSH port (default 22)
- Root credentials or SSH key path
For CLI-driven migrations (recommended for batch jobs or automation), create a config file:
# /root/migration.ini
[source]
type = cpanel
host = <cpanel-server-ip>
port = 22
login = root
password = <password> # or use key= /root/.ssh/id_rsa
[migration]
migrate-domains = all
# or list specific domains:
# migrate-domains = example.com,client2.com
Run the pre-migration check before transferring anything:
/usr/local/psa/admin/sbin/modules/panel-migrator/plesk-migrator check --config /root/migration.ini
Resolve every ERROR in the output before proceeding. Warnings about Apache modules are expected — Plesk uses nginx+Apache and some cPanel Apache modules have no Plesk equivalent.
💡 None of these worked? Skip the guesswork.
Get Expert Help →Running the Account Transfer: Step-by-Step with CLI Commands
Run the main migration command. This transfers hosting accounts, email accounts, databases, DNS zones, and SSL certificates:
/usr/local/psa/admin/sbin/modules/panel-migrator/plesk-migrator transfer-accounts --config /root/migration.ini --output-log /var/log/plesk_migration.log
For large servers this can take several hours. Monitor progress:
tail -f /var/log/plesk_migration.log
Run a re-sync immediately before DNS cutover to capture any files written to cPanel after the initial transfer:
/usr/local/psa/admin/sbin/modules/panel-migrator/plesk-migrator copy-content --config /root/migration.ini --output-log /var/log/plesk_migration_resync.log
List all subscriptions and spot-check counts:
plesk bin subscription --list | wc -l
Compare this to the cPanel account count:
whmapi1 listaccts | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d['data']['acct']))"
# On Plesk server, list all MySQL databases
mysql -u admin -p"$(cat /etc/psa/.psa.shadow)" -e "SHOW DATABASES;" | grep -v "^Database\|information_schema\|performance_schema\|mysql\|psa\|horde"
On the authoritative nameserver, set all A and MX records to TTL 300 (5 minutes). This ensures old resolvers expire their cached values quickly once you switch.
On your local machine, add a temporary hosts entry pointing the domain to the Plesk server IP. Verify the website, email login pages, and SSL certificate before touching DNS:
# On your local machine
echo "203.0.113.10 example.com www.example.com" | sudo tee -a /etc/hosts
curl -I https://example.com # Should return 200 and correct SSL
Change all A records (root domain and www) to the Plesk server's IP. If Plesk manages DNS itself, update the nameserver delegation at the registrar.
Point MX records to the Plesk server's hostname. Confirm Plesk's mail server is accepting connections:
telnet <plesk-ip> 25
watch -n 10 "dig +short @8.8.8.8 example.com A"
Once the dig response shows the Plesk IP, propagation is complete for Google's resolver. Most users will follow within 5–15 minutes given the TTL 300.
Post-Migration Validation: Websites, Databases, Email, and SSL
After DNS cutover, work through this validation checklist:
Website Validation
# Check HTTP response for every migrated domain
for domain in $(plesk bin subscription --list | awk '{print $1}'); do
code=$(curl -o /dev/null -s -w "%{http_code}" "https://$domain")
echo "$domain: $code"
done
Database Validation
# Verify row counts match source for critical tables
mysql -u admin -p"$(cat /etc/psa/.psa.shadow)" -e "SELECT table_schema, COUNT(*) tables, SUM(table_rows) rows FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','psa','horde') GROUP BY table_schema;"
Email Validation
Send a test email from each domain and confirm IMAP delivery. Check Plesk's mail queue is clear:
plesk bin mail_pref --list # Confirm mailboxes exist
/usr/sbin/postqueue -p # Queue should be empty or small
SSL Certificate Validation
for domain in $(plesk bin subscription --list | awk '{print $1}'); do
expiry=$(echo | openssl s_client -connect "$domain:443" -servername "$domain" 2>/dev/null | openssl x509 -noout -dates 2>/dev/null | grep notAfter)
echo "$domain: $expiry"
done
Manual Post-Migration Fixes (Not Transferred by Plesk Migrator)
This is the section most guides omit. After automated migration, you must manually reconfigure:
Fail2Ban:
# Install Fail2Ban on Plesk server
apt-get install fail2ban -y # Debian/Ubuntu
# or
yum install fail2ban -y # CentOS/AlmaLinux
# Recreate your jails
cat > /etc/fail2ban/jail.local <<'EOF'
[sshd]
enabled = true
port = ssh
maxretry = 5
bantime = 3600
findtime = 600
[plesk-panel]
enabled = true
port = 8443,8880
filter = plesk-panel
maxretry = 5
bantime = 3600
EOF
systemctl enable fail2ban
systemctl restart fail2ban
ModSecurity:
# In Plesk GUI: Extensions > ModSecurity > Install
# Then in Tools & Settings > Web Application Firewall, enable detection mode
# Apply OWASP CRS ruleset or paste your custom rules
# Enable enforcement mode only after testing in detection mode for 48h
PHP Handler (CLI vs FPM vs FastCGI):
# Set PHP handler for a domain via Plesk CLI
plesk bin domain --set example.com -php_handler_id plesk-php82-fpm
# List available PHP handlers
plesk bin php_settings --list-handlers
Firewall Rules:
# Restore from your saved iptables snapshot
iptables-restore < /root/cpanel_iptables_backup.rules
# Or configure via Plesk Firewall extension:
# Tools & Settings > Firewall > Enable Firewall Rules Management
SpamAssassin Bayes Database:
# The Bayes DB is NOT migrated. Retrain from scratch:
sa-learn --spam /path/to/known-spam-maildir
sa-learn --ham /path/to/known-ham-maildir
sa-learn --dump magic # Verify token counts
Cron Jobs (Server-Wide):
Per-domain cron jobs migrate successfully. Server-wide cron tasks from cPanel's Tools & Settings > Scheduled Tasks do not. Review /etc/cron.* on the cPanel server and recreate any server-level jobs on Plesk via Tools & Settings > Scheduled Tasks.
For expert help managing this transition, see our CloudHouse server migration service — our team handles full cPanel-to-Plesk migrations with guaranteed zero data loss.
FAQs
Conclusion
Migrating from cPanel to Plesk using Plesk Migrator is reliable for accounts, emails, databases, DNS zones, and SSL certificates — but the tool's documented limitations mean you must manually recreate Fail2Ban jails, ModSecurity rules, PHP handler assignments, firewall rules, and SpamAssassin's Bayes database afterward. Follow the DNS cutover sequence (lower TTL early, test via /etc/hosts, switch, monitor), run the delta re-sync immediately before cutover, and validate every domain with curl after the switch. The result is a fully migrated server at a fraction of the ongoing cPanel licensing cost.
