Skip to content
In this postMariaDBMySQL

Moving a MySQL database off cPanel shared hosting

14 min readMySQLMariaDBMigrationDatabases

Short version: take a real dump rather than a phpMyAdmin export if you have shell access, create a managed MariaDB (it speaks the MySQL wire protocol, so your dump and your drivers do not change), load the file, and repoint the app at a new host, port, user and password. The two things that surprise people are that the new database is not on port 3306 and that TLS is not optional. The two things that genuinely get worse are that your web server and your database are no longer on the same machine, and that you now have two bills.

Shared hosting is not a scam and this is not a post about how bad it is. It is a post about the specific moment when the database is the thing holding you back, which usually announces itself in one of four ways.

You cannot connect to it from anywhere else. Shared hosts bind MySQL to localhost by default. Your local development machine, a CI job, a script on a laptop, a new front end on a different host: none of them can reach it. Panels expose a "Remote MySQL" screen where you list the hosts allowed in, and it works, and it is also the moment you realize that reaching your own data is a feature the host grants you.

The version is not yours to choose. Older shared hosting is still serving MySQL 5.7, sometimes 5.6, occasionally a MariaDB 10.x from the middle of the last decade. That decides which SQL you can write, which character sets you have, and which security fixes you are running.

The backup is theirs. The panel takes one. You have probably never restored from it. If you have never restored from it, you do not have a backup, you have a file.

The database dies with the hosting plan. It is not a separate thing you own. When the plan lapses, or you outgrow the PHP side and move the app to a platform host, the database is the piece with nowhere to go.

If any of those are why you are here, the rest of this is the move.

Step 1: find out what you actually have

Do this first. Every decision after it depends on the answers, and all four queries run fine in phpMyAdmin's SQL tab.

sql
SELECT VERSION();
sql
SELECT TABLE_NAME, ENGINE, TABLE_ROWS,
       ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 1) AS mb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC;
sql
SELECT DISTINCT CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND CHARACTER_SET_NAME IS NOT NULL;
sql
SELECT COUNT(*) FROM information_schema.VIEWS    WHERE TABLE_SCHEMA = DATABASE();
SELECT COUNT(*) FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE();
SELECT COUNT(*) FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = DATABASE();

What you are looking for:

  • Total size. Add up the mb column. Most databases that live on shared hosting are under a gigabyte, which means the whole move is a file transfer and an evening.
  • Any engine that is not InnoDB. MyISAM tables cannot be dumped consistently while writes are happening. They are common on databases that predate 2015.
  • Any character set that is not utf8mb4. A column declared utf8 on an old MySQL is utf8mb3, three bytes per character, and cannot hold emoji. It moves across without complaint and stays broken.
  • Views, routines and triggers. If those three counts are all zero, your move just got much simpler. If they are not, keep reading past step 2, because the default dump command leaves two of the three behind.

Step 2: get a dump that is actually complete

There are three ways off a cPanel-style host, and they are not equivalent.

With SSH access, use mysqldump. This is the only option that gives you a file you can trust for a large database.

bash
mysqldump \
  --single-transaction \
  --no-tablespaces \
  --routines --events \
  --default-character-set=utf8mb4 \
  -u DBUSER -p DBNAME > dump.sql

--single-transaction takes a consistent snapshot without locking the tables, which is what lets the site stay up during the dump. It only works for InnoDB, which is why step 1 asked about engines. --no-tablespaces is there because the shared-hosting account does not have the PROCESS privilege and the dump errors without it. --routines --events is there because stored routines and events are not included by default, and their absence produces no error at all.

If your local mysqldump is from MySQL 8 and the server is older or is MariaDB, you will also need --column-statistics=0. What actually breaks when you import a MySQL dump into MariaDB is the reference for that error and the others worth knowing before you start.

Without SSH, export from phpMyAdmin, carefully. Export tab, Custom, and then: format SQL, compression gzipped, and under Object creation options make sure procedures, functions, views and triggers are all ticked. The gzip setting matters more than it looks. The failure mode of a phpMyAdmin export is not an error, it is a PHP execution timeout that hands you a file which simply stops in the middle of a table. Compressing reduces the odds, and so does exporting the largest few tables one at a time.

Then check the file yourself. A complete mysqldump file ends with a "Dump completed on" comment line. Open the last few lines of the download and confirm it is there. If it is not, the export truncated and everything downstream is built on a lie.

The cPanel backup wizard is a third option and produces a .sql.gz per database. It is fine. It is also the least controllable, so if the file turns out to be short you have fewer knobs to turn.

Step 3: create the target

MariaDB is the drop-in. It speaks the MySQL wire protocol, your mysqldump output restores into it unchanged, and mysql2, Prisma, Drizzle, PDO and the mysql command line client all connect without a code change. MySQL vs MariaDB covers where the two engines genuinely diverge, and none of it is on the connection path.

On Layerbase specifically, MariaDB is the one on the free tier: $0/mo with no card, 2 databases, 5 GB of storage, up to 20 concurrent connections. MySQL is available too, starting on the Solo plan at $5/mo. If you are moving a MySQL database and have no attachment to the vendor, create a MariaDB and skip the decision.

The version dropdown is worth one second of thought. Take the current default rather than the oldest option: the older LTS series predates the MySQL collation aliases, which is the difference between a dump that restores and a dump that stops on the first CREATE TABLE. Managed MariaDB has the version list and what each one includes.

Step 4: load the file

You will get a host, a port, a user, a password and a database name from Quick Connect. The port is a per-database allocated TLS port, not 3306, because there is no shared port to hand out. Copy it exactly.

bash
mysql --ssl-mode=REQUIRED \
  -h your-host.cloud.layerbase.dev -P PORT -u USERNAME -p DBNAME < dump.sql

If the dump is gzipped, do not decompress it first:

bash
gunzip -c dump.sql.gz | mysql --ssl-mode=REQUIRED \
  -h your-host.cloud.layerbase.dev -P PORT -u USERNAME -p DBNAME

Two things can stop this. Error 1273 on an unknown collation means the target server is older than the dump's source. Error 1227 asking for SUPER means your dump contains views or routines stamped with the old server's account name, which is a one-line sed away from fixed. Both are covered in the import errors post.

If your host does expose Remote MySQL, there is a shorter path. Open the import wizard at cloud.layerbase.com/create, choose MySQL or MariaDB as the source, and paste a mysql://user:password@host:3306/dbname connection string. It runs the dump and the load once, against a source it only reads. Most shared hosts want you to name the connecting address before they let anything in, so this works when you can add an access host and does not when you cannot. The dump file path above always works.

Step 5: repoint the app

Four values change: host, port, user, password. In a .env file that is one line.

bash
DATABASE_URL="mysql://USERNAME:PASSWORD@your-host.cloud.layerbase.dev:PORT/DBNAME?ssl=true"

Three details that catch people out, in the order they catch them.

TLS is required, and some clients need telling. Connections run through a pooler that will not accept a plaintext connection. Node's mysql2 wants ssl: {} in the config or ?ssl=true in the URL. PHP's PDO and mysqli need the MySQL client's SSL mode set rather than assumed. A client that silently tries plaintext first reports this as a connection refusal, which sends people hunting for a firewall problem that is not there.

The port is not 3306, and some config formats hide that. Anything that takes a bare host string and appends :3306 needs the host written as host:port. That includes a lot of older PHP application config.

Your old username is gone. Panels create per-application accounts with prefixed names, and the new database gives you one generated account. Grep the codebase for the old username, not just for the old hostname. Config files, cron scripts, and a backup script nobody has read in two years are the usual hiding places.

Step 6: check it before you cut over

Row counts alone will not catch a truncated dump, because a file that stopped at table 40 of 60 gives you perfect counts for the first 39. Compare object counts too:

sql
SELECT COUNT(*) FROM information_schema.TABLES   WHERE TABLE_SCHEMA = DATABASE();
SELECT COUNT(*) FROM information_schema.VIEWS    WHERE TABLE_SCHEMA = DATABASE();
SELECT COUNT(*) FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA = DATABASE();
SELECT COUNT(*) FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = DATABASE();

Run them on both servers and compare the four numbers. Then take the three largest tables and compare COUNT(*) and MAX(id) on each side, and exercise the application against the new database with the old one still running, so that rolling back is a matter of putting one line of config back.

Leave the old database in place for a week after you switch. It costs you nothing and it is the only rollback that does not involve a restore.

The thing to know about free databases and live sites

This is the part I would want told to me plainly rather than discovered.

A free database on Layerbase hibernates after 60 minutes with no connections, and MariaDB is the engine where we tell people not to rely on the first connection to wake it. Its cold start runs longer than most client connect timeouts, so the honest instruction is to start it from the dashboard, or keep it awake on a paid plan. That is fine for a staging copy, a database you query from a laptop, or the week you spend testing the move. It is not what you want under a site that has visitors at 3am.

For a live site, Solo at $5/mo is the plan that fits: 2 databases, 10 GB of storage, uncapped connections, daily backups on a 7-day rolling window, and an always-on allocation you pin the database to so it never sleeps. Pro at $15/mo raises that to 10 databases, 25 GB, and 30 days of backups. The free tier is a real free tier with no expiry rather than a trial, and it is genuinely the right place to land the first copy of your data while you are still checking it.

Left alone entirely, a hibernated free database is archived after 14 days (7 if you never connected to it). The data and backups are kept and restoring takes a click. Nothing is deleted out from under you, which is the other half of the same promise.

What the shelf costs

Worth doing the arithmetic before you assume the move is an upgrade in price as well as in control. All competitor figures verified 2026-08-26 from the vendor's own pricing page.

OptionPriceWhat that buys
Hostinger Premium shared hosting$2.99/mo on a 48 month term, renews at $10.99/mo10 databases, 3 GB each, MySQL on the same box as the site
Layerbase Free$0, no card2 databases, 5 GB, hibernates when idle
Aiven Developer, MySQL$5/mo1 CPU, 1 GB RAM, 8 GB storage
Layerbase Solo$5/mo2 databases, 10 GB, one pinned always-on
Clever Cloud XXS Small, MySQL5.00 EUR/mo512 MB RAM, 512 MB database size
DigitalOcean Managed MySQL$15.15/mo1 GiB RAM, 1 vCPU, 10 GiB storage, single node
PlanetScale, MySQL on Vitess$39/moPS-10, 1 GiB memory, three-node HA

Sources: Hostinger pricing and their plan limits page, Aiven MySQL pricing, Clever Cloud MySQL docs, DigitalOcean managed databases, PlanetScale pricing.

The shared-hosting number people quote at each other is an introductory rate on a four year prepayment, and the renewal is roughly 3.7x it. PlanetScale's widely repeated $5 entry price is their Postgres SKU, not MySQL: the MySQL floor on Vitess is $39/mo, and there has been no free tier there since Hobby closed, which we wrote about separately. Aiven's free MySQL is real and does not expire, but it powers off after a period of inactivity, so it sits in the same category as our free tier rather than opposite it.

What shared hosting still does better

None of these are trivial, and a couple of them are the reason people move back.

Your PHP and your database were on the same machine. Every query was a loopback call. After the move they are a network round trip apart. For an application that issues a handful of queries per page this is invisible. For one that issues eighty, it is not, and no managed provider fixes that for you. If your app is chatty, the move is a good moment to look at why.

One bill and one login. You are adding a second vendor and a second thing to remember. That is a real cost even when the dollar amount is small.

The panel did things you have not thought about. Cron jobs, mail, DNS, the file manager. Moving the database moves the database. Everything else stays where it was, which is either exactly what you wanted or a surprise, depending on how carefully you read this paragraph.

It is cheap for what it is, at least on the first term. A small site with one database, no remote access requirement, and no ambitions beyond staying up is well served by shared hosting, and the answer to "should I move" is sometimes no. Read the renewal rate rather than the sticker before you conclude that, though.

The case for moving is strongest when you want the database reachable from more than one place: a local development copy, a CI job, a preview deploy, an agent, a second service. That is the thing shared hosting structurally cannot give you, and it is why most people who make this move never go back.

FAQ

How do I export a MySQL database from cPanel?

Three ways. With SSH access, mysqldump --single-transaction --no-tablespaces --routines --events gives you the most reliable file. Without SSH, use phpMyAdmin's Export tab in Custom mode with gzip compression and every object type ticked. The cPanel backup wizard also produces a .sql.gz per database. Whichever you use, check that the file ends with the "Dump completed on" line before trusting it.

Can I move a MySQL database to MariaDB without changing my app?

Usually yes. MariaDB speaks the MySQL wire protocol, so your drivers, your ORM and your mysqldump file all work unchanged. What changes is connection configuration: a new host, a port that is not 3306, a new username and password, and TLS. The engine-level differences are real but sit well away from the connection path.

Why can I not connect to my shared hosting database remotely?

Because the host binds MySQL to localhost and only allows connections from addresses you explicitly list. Most panels expose this as a "Remote MySQL" screen where you add an access host. It is the single most common reason people start looking for somewhere else to put the database.

What breaks when I move a MySQL database to a new host?

The predictable list: DEFINER clauses on views and routines that name an account which does not exist on the new server, users and grants that were never in the dump file, utf8mb3 columns that import cleanly and then cannot store emoji, and stored routines and events that are silently omitted unless you asked for them. The import errors post covers each one and its fix.

Is there a free place to put a MySQL database?

MariaDB is on our free tier at $0/mo with no card: 2 databases, 5 GB of storage, and no expiry date. It is MySQL wire compatible, so a MySQL dump lands in it unchanged. Free databases hibernate after an hour idle, which makes them right for a staging copy or the testing phase of a move and wrong for a site with live traffic. Every free database tier that sleeps, pauses, or expires compares how the various providers handle that.

Do I have to move my website too?

No, and mostly you should not do both at once. Point the existing application at the new database, confirm it works, and leave the web hosting alone. We host the database, not PHP, so the site stays where it is either way. Moving one thing at a time also means you always know which change broke something.

Where to start

Run the four queries in step 1 against your current database. They take a minute and they decide everything else.

Then create a MariaDB database on the free plan, load a copy of the dump into it, and point a local checkout of your app at it before you touch anything in production. If the app works against the copy, the real cutover is a config change and a five minute window. If it does not, you found out on a copy.