Legacy PHP
MySQL 5 to 8 migration checklist
A database migration can look finished when it isn’t: rows arrive, the application starts, and a total is wrong three weeks later. This is the path we would follow from MySQL 5.x to 8, with the checks at each stage.
By itmarkerz Technologies. Updated 30 October 2026.
Where you are, and the route from there
| Starting version | Route | Basis |
|---|---|---|
| 5.7.9 or later | Latest 5.7, then 8.0, then 8.4 | Supported in place |
| 5.6 | 5.7 first, then as above | Skipping 5.7 is not supported |
| 5.5 or older, including 5.0 | Export and import into a new server, usually through a staging copy | The manual doesn’t describe a direct path. Stepping through each old release is rarely practical, so we treat this as a data migration. |
Two dates to plan around. Oracle moved MySQL 5.7 to Sustaining Support on 25 October 2023, and its documentation says MySQL 8.0 reached end of life in April 2026. So 8.0 is now a stop on the way rather than a destination. MySQL 8.4 can be reached from 8.0, not directly from 5.7.
Before you touch anything
- Take a full backup and restore it somewhere else. A backup you haven’t restored is only a hope.
- Record row counts and totals for tables that hold money, stock and customers, so you have something to compare.
- List everything that connects: the application, reports, cron jobs, spreadsheets, other systems.
- Note the character set of the server, each database, table and column. They often differ on old systems.
- Save the current
sql_modeand configuration file. You will want to compare them later.
Run the automated checks
MySQL Shell’s upgrade checker covers 5.7 and later, and looks for reserved keywords, removed functions, zero dates, old temporal columns, utf8mb3 and authentication problems among other things. It doesn’t support anything older than 5.7, so on an older system, run it against your staging copy once that reaches 5.7.
mysqlsh -- util checkForServerUpgrade user@localhost:3306 --target-version=8.4.0 --output-format=JSON
mysqlcheck -u root -p --all-databases --check-upgradeFix these before the upgrade
- Old temporal columns. In-place upgrade isn’t supported if TIME, DATETIME or TIMESTAMP columns are still in the pre-5.6.4 format. Upgrade them with REPAIR TABLE first.
- Partitioned tables on an engine without native partitioning, such as older MyISAM tables. Convert them to InnoDB or remove the partitioning.
- Long names and values. Foreign key constraint names over 64 characters, and ENUM or SET elements over 255 characters or 1,020 bytes, cause the upgrade to fail.
- Reserved words. Some names that were fine in 5.7 are reserved in 8.0. A column called
rankis a typical case. Quote identifiers with backticks. - lower_case_table_names. Don’t plan to change it at upgrade time. In 8.0 the server won’t start with a value different from the one it was initialised with.
- Removed SQL modes. Take
NO_AUTO_CREATE_USERout of your configuration, or the 8.0 server won’t start.
Defaults that change how your application behaves
| Change | What you may see | What to do |
|---|---|---|
| Strict sql_mode, including NO_ZERO_DATE and ONLY_FULL_GROUP_BY | Inserts of 0000-00-00 are refused. GROUP BY queries that used to run now return errors. | Fix the queries and clean the data. Loosening the mode postpones the problem, and the zero-date modes are themselves deprecated. |
| GROUP BY no longer sorts through ASC or DESC | Reports come back in a different order. | Add an explicit ORDER BY to any query whose order matters. |
| Default character set is utf8mb4 with utf8mb4_0900_ai_ci | Existing tables keep their settings, but new tables differ, and joins across them can fail or slow down. | Choose the character set and collation deliberately, and convert in a planned step. |
| caching_sha2_password becomes the default login method | Older drivers can’t connect. mysql_native_password is off by default in 8.4 and removed in 9.0. | Move application accounts and update the driver. The manual calls reverting a temporary measure. |
| explicit_defaults_for_timestamp is on | TIMESTAMP columns stop getting automatic defaults you may have relied on. | Declare defaults in the schema, and test inserts. |
| local_infile is off, max_allowed_packet is 64 MB, binary logging is on | Bulk imports fail, and disk use grows from binary logs. | Set these deliberately, and check disk capacity. |
The full lists are in the manual: changes in MySQL 8.0 and changes in MySQL 8.4.
Prove the data survived
This is the part most checklists leave out. On the cabinet manufacturer’s move from MySQL 5.0 to 8, we validated data at every step and checked workflows against the old system in parallel. A check like that can include:
- Row counts per table, old against new.
- Sums on money and quantity columns, grouped by month or by customer, so an error can be located.
- Text comparison on a sample with accents and symbols, because encoding damage is invisible in counts.
- Date checks on any column that held zero dates, and a written decision on what those values mean now.
- Real workflows run on both systems with the same input, comparing the results.
Write down how you will decide to go ahead before you run the cutover, and keep the old server untouched until you have decided.
Cutover and the first week
- Rehearse the whole migration on a copy, and time it. That time sets your maintenance window.
- Stop writes to the old database during the final copy, and record the moment you did.
- Keep a tested way back, and decide in advance how long it stays valid once new data starts arriving.
- Watch slow queries for the first week. 8.0 and 8.4 change defaults that can shift query plans.
- Run your reports early. They tend to use the oldest SQL in the building.
A database move rarely happens on its own, since old MySQL usually comes with old PHP. See the PHP upgrade matrix for that side, and MySQL development if you only need database work.
Database migrations in context
Cabinet manufacturer's ordering, dealer-portal and inventory system moved from PHP 5.6 and MySQL 5.0 to Laravel 13 on PHP 8.5 and MySQL 8, with a new Next.js front end. Eight months, workflows preserved, now live.
- Sector
- Manufacturing
- Region
- Canada and US
- Stack
- Laravel 13, PHP 8.5, MySQL 8, Next.js
- Engagement
- Upgrade, about eight months, live
Loyalty-card management system we first built on PHP 5.6, re-platformed onto Laravel with all cards, balances and rules preserved. Two months, live since mid-2026, Indian client.
- Sector
- Retail & loyalty
- Region
- India
- Year
- 2026
- Stack
- Laravel
- Engagement
- Upgrade of our own earlier build, two months
Questions
Can I upgrade directly from MySQL 5.6 to 8.0?
Not as a supported in-place upgrade. The MySQL manual says skipping versions isn't supported and gives 5.6 to 8.0 as its example. You go to a 5.7 GA release (5.7.9 or higher) first, preferably the latest one.
Should the target be MySQL 8.0 or 8.4?
MySQL 8.4. Oracle's documentation says MySQL 8.0 reached end of life in April 2026, and 8.0.46 was its last release. You still pass through 8.0, because a direct upgrade from 5.7 to 8.4 isn't supported.
What did you find when you moved MySQL 5.0 to MySQL 8?
We validated data at every step on that project, but we aren't publishing the client's specific findings. The checklist here is built from the MySQL manual and from how we approach this work, not from a count of errors on one system.
More on legacy PHP
- PHP end-of-life dates and the upgrade path from 5.6 to 8.5Every branch date from php.net, and what breaks at each step.
- PHP 5.6 to PHP 8: what breaks, and four ways offThe failures you will meet first, and how to choose a route.
- Rewrite or upgrade a legacy PHP system?Three real projects, three different answers.
- What it costs to modernise a legacy PHP systemOur published price ladder for this work, and what moves a quote.
Moving a database you can’t afford to get wrong?
Tell us your MySQL version and roughly what the system holds. We will start by reading the schema.
We reply within one business day (IST).