Slot 42 Plesk troubleshooting
Plesk Migrator Failed: MySQL, SSH Key & MariaDB 11.4 Errors
Plesk Migrator fails on three things: MySQL socket connections, SSH key authentication, and MariaDB 11.4 dumps. Here is how to diagnose and fix each one.
Plesk Migrator fails in three recognisable ways, and the message on screen rarely names the real one. A database connection error usually means a socket path. An SSH error usually means a key the Migrator cannot use unattended. Anything odd on a recent source is worth checking against MariaDB’s version changes before you blame the tool. I have lost afternoons to all three.
Read the migration logs first
Do not troubleshoot from the extension’s summary screen. Every run writes a session directory on the destination:
ls -lt /usr/local/psa/var/modules/panel-migrator/sessions/
cd /usr/local/psa/var/modules/panel-migrator/sessions/<SESSION_ID>/
ls -l
The session directory holds the main log, a verbose debug log and per-subscription logs. The debug log is the one that matters. It records the shell commands the Migrator ran on the source and their stderr, which is where the real error text lives. The UI summary is a paraphrase.
tail -f /usr/local/psa/var/modules/panel-migrator/sessions/<SESSION_ID>/debug.log
grep -iE 'error|denied|refused|failed' /usr/local/psa/var/modules/panel-migrator/sessions/<SESSION_ID>/debug.log | tail -40
From the command line, the binary and its help live here:
plesk sbin modules/panel-migrator/plesk-migrator --help
Panel-level problems that are not migration-specific end up in /var/.
Family 1: MySQL connection failures
The “unable to connect to the database server” error is almost always one of four things, and you can test all four by hand in under a minute.
First, socket versus TCP. The MySQL client treats localhost and 127.0.0.1 as different transports: localhost goes through a unix socket file, 127.0.0.1 opens TCP to port 3306. If the source’s my.cnf names a socket path the running server never used, every localhost connection fails while TCP works. Compare the two:
mysql -e "SHOW VARIABLES WHERE Variable_name IN ('socket','bind_address','port','version')"
grep -rnE '^\s*(socket|bind-address)' /etc/my.cnf /etc/my.cnf.d/ /etc/mysql/ 2>/dev/null
ls -l /var/lib/mysql/mysql.sock /var/run/mysqld/mysqld.sock 2>/dev/null
Debian and RHEL family systems disagree about that path, and a server rebuilt from one to the other keeps the old value in a config file nobody looks at. Point [client] socket at the real file, or force TCP by using 127.0.0.1.
Second, bind-address. If the source’s database sits on a separate host, bind-address = 127 means nothing outside that machine can reach it, regardless of grants. It needs the interface address (or 0.0.0.0 behind a firewall rule) and a restart.
Third, grants. For a remote database server the migration user needs rights from the connecting host, not from localhost:
CREATE USER 'migrator'@'DEST_IP' IDENTIFIED BY 'strong-password';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER, PROCESS, RELOAD ON *.* TO 'migrator'@'DEST_IP';
FLUSH PRIVILEGES;
SHOW GRANTS FOR 'migrator'@'DEST_IP';
Fourth, stored credentials on a Plesk source. Plesk keeps its own admin database password, and if it has drifted the Migrator cannot authenticate even though your shell session works. On the source:
plesk db -e "SELECT VERSION()"
If that fails, the source’s Plesk has a credentials problem and the Migrator will never get past it.
Then reproduce the exact connection the Migrator makes, from the destination:
mysql -h SOURCE_IP -P 3306 -u migrator -p -e "SELECT VERSION(), @@socket"
and locally on the source, both ways:
mysql -h 127.0.0.1 -P 3306 -u admin -p -e "SELECT 1"
mysql -S /var/run/mysqld/mysqld.sock -u admin -p -e "SELECT 1"
Whichever of those fails is your bug, and you now have the real error text instead of the extension’s summary.
Family 2: SSH key authentication failures
The Migrator runs unattended as root on the destination. That single fact explains most key failures.
People expect it to prompt for a passphrase or to pick up a loaded ssh-agent identity. It has no terminal, so a passphrase-protected key just fails. Use a key with no passphrase, root-only, deleted after the migration. To strip the passphrase off an existing key:
ssh-keygen -p -f /root/.ssh/plesk_migrator # set an empty new passphrase
# or generate a fresh one
ssh-keygen -t ed25519 -N '' -f /root/.ssh/plesk_migrator -C 'plesk migration'
ssh-copy-id -i /root/.ssh/plesk_migrator.pub -p 22 root@SOURCE_IP
OpenSSH silently refuses keys with loose permissions:
chmod 700 /root/.ssh
chmod 600 /root/.ssh/plesk_migrator
chmod 644 /root/.ssh/plesk_migrator.pub
chown -R root:root /root/.ssh
restorecon -Rv /root/.ssh 2>/dev/null # if SELinux is enforcing
On the source, /root/ must be 600 and owned by root, and /root itself must not be group-writable.
A first connection to an unknown host asks for confirmation, and with nobody there to type “yes” that prompt is a failure. Prime the known hosts file as root on the destination first, but do not pipe ssh-keyscan straight into it. Whatever answers on that IP is what you would be trusting, and this is a root login to a server you are about to copy every customer’s data off. ssh-keyscan itself carries the warning: it gives you no protection at all against a machine in the middle.
Scan into a scratch file, print the fingerprint, and compare it with one you got some other way. The provider’s console, an existing session on the source, or a colleague reading it down the phone all count; another connection to the same IP does not.
Scan one key type, not all of them. Left alone ssh-keyscan fetches every type it knows, so you end up holding several keys with an out-of-band fingerprint for exactly one of them, and appending the file trusts the rest on the word of whatever answered on that IP. -t ed25519 fetches only the key you are about to verify. Use mktemp for the file too, because /tmp is world-writable and a fixed name in it is a name another user can be sitting on before you are:
KEYS=$(mktemp)
ssh-keyscan -t ed25519 -p 22 SOURCE_IP > "$KEYS"
ssh-keygen -lf "$KEYS"
On the source, from a session you already trust:
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
Only when the two fingerprints match does that one key go in the file:
cat "$KEYS" >> /root/.ssh/known_hosts && rm -f "$KEYS"
Set a non-standard port in the Migrator’s source settings rather than relying on a ~/.ssh/config entry. On the source, confirm sshd will accept what you are sending:
grep -iE '^(Port|PermitRootLogin|PubkeyAuthentication|AllowUsers|AuthorizedKeysFile)' /etc/ssh/sshd_config
PermitRootLogin prohibit-password accepts keys but rejects passwords, which catches people who switch to password auth as a fallback.
Then prove it end to end, exactly as the Migrator would:
ssh -i /root/.ssh/plesk_migrator -p 22 -o BatchMode=yes root@SOURCE_IP 'id; hostname'
BatchMode=yes is the flag that matters. It forbids any prompt, so if this succeeds the Migrator’s connection will too. When it fails, run the same command with -vvv and read the lines around the offered key.
Family 3: MariaDB 11.4 compatibility
Newer MariaDB on either end breaks assumptions the Migrator was built around, and the symptoms look nothing like a version problem.
Modern MariaDB installs commonly authenticate the root account through the unix socket plugin rather than a password hash. A password-based root connection then fails even with the correct password. Check what each account actually uses:
SELECT User, Host, JSON_VALUE(Priv, '$.plugin') AS plugin FROM mysql.global_priv;
mysql.global_priv has three columns and plugin is not one of them. It holds Host, User and a JSON Priv blob, and everything else people expect to select lives inside that blob. Ask for plugin directly and MariaDB answers “unknown column”, which reads like the wrong table name rather than the wrong shape. JSON_VALUE returns the bare string; JSON_EXTRACT returns it with the JSON quotes still on, which then fails every comparison you write against it.
If root shows a socket-based plugin, use a dedicated password-authenticated migration user instead of root, or the Plesk admin account.
The dumps themselves are the second trap. MariaDB 11.x dump output carries a sandbox-mode directive on line 1. Older clients on the receiving end reject the import outright, which reads like a corrupt dump rather than a version mismatch. Strip the line and it imports normally:
head -1 /root/dbname.sql
sed -i '1{/sandbox/d}' /root/dbname.sql
Beyond that first line, dumps from a newer server can carry syntax and character set declarations an older destination rejects, so always confirm which direction you are moving:
mysql -V # run on both servers before you start
Moving forward a version is normally fine. Moving backwards is not supported and the Migrator has no special handling for it.
When the database side is the only thing failing, stop fighting it. Let the Migrator handle files, mail, DNS and subscription configuration, and move the databases by hand:
# on the source
mysqldump --single-transaction --routines --triggers --events \
--hex-blob --default-character-set=utf8mb4 dbname > /root/dbname.sql
scp -P 22 /root/dbname.sql root@DEST_IP:/root/
# on the destination, create it through Plesk so the panel registers it
plesk bin database --create dbname -domain example.com -server localhost \
-add_user dbuser -passwd 'strong-password'
mysql dbname < /root/dbname.sql
Create the database through Plesk, not straight in MySQL. A database imported directly exists on disk but never appears in the customer’s control panel, and nobody notices until they open phpMyAdmin. Then check every wp-config.php and application config for the old database name and credentials.
Decision table
| Symptom | Likely cause | Fix |
|---|---|---|
| Cannot connect to database server, source is local | Socket path in my.cnf does not match the running server |
Correct socket=, or connect via 127.0.0.1 |
| Connects locally, fails from destination | bind-address or firewall on port 3306 |
Bind to the interface, open the port to the destination IP only |
| Access denied for the migration user | Grants issued for localhost, not the destination host |
Re-grant for 'user'@'DEST_IP' |
| SSH fails immediately, no prompt | Key has a passphrase, or wrong permissions | Passphrase-free key, 600 on the key, 700 on .ssh |
| SSH fails only from the Migrator | Host key never accepted for root | ssh-keyscan into /root/ |
| Root password rejected but correct | Socket auth plugin on newer MariaDB | Use a password-authenticated migration user |
| Import fails on the first line of a dump | MariaDB 11.x sandbox directive | Strip line 1, or dump with a matching client version |
| Files migrate, databases never do | Version gap the Migrator cannot bridge | Migrate databases manually, keep the Migrator for everything else |
When to abandon the Migrator
Two rules. If the same subscription fails twice for the same reason after you have fixed the named cause, the Migrator is not going to succeed on the third attempt. And if the source panel or database is old enough that the destination cannot import its dumps directly, plan a manual migration from the start rather than discovering it at hour four.
Manual here means mysqldump for databases, rsync for document roots and mail, then rebuilding subscriptions and DNS on the destination. It is more steps, but every step is observable, and at 3am that is worth a lot. The same reasoning applies to a stalled WHM Transfer Tool session.
FAQ
Can Plesk Migrator use a key with a passphrase?
No. It runs unattended with no terminal to prompt on. Use a passphrase-free key that exists only for the migration and delete it from both servers afterwards.
Why does it connect over SSH but still fail on databases?
SSH access and database access are separate credentials. The Migrator reaches the source shell fine, then the database client it runs there fails on a socket path, a grant or an auth plugin. The debug log shows the exact command that failed.
Does the Migrator move mail as well as files?
It handles mail for supported source panels, but mailbox format differences are where I see silent losses. Verify message counts per mailbox after the run rather than trusting a green tick, and fall back to imapsync for anything that looks short.
Can I split the job between the Migrator and manual steps?
Yes, and it is usually the right call: let the Migrator do files and configuration, then import databases by hand. That turns one opaque failure into two problems you can watch.
Where this fits
Plesk Migrator is a decent tool with three blind spots. Check those first and a lost afternoon becomes a ten-minute diagnosis. If you would rather not spend the afternoon at all, cross-panel migration work is what I do, including the parts the official tools skip.