TechEarl

How to Migrate from MySQL 5.7 to 8.0 (Step-by-Step)

MySQL 5.7 has been past its EOL since October 2023. Here is the migration to 8.0: prerequisites, dry-run with mysqlcheck, in-place upgrade, the authentication-plugin change that breaks old clients, and rollback if it goes sideways.

Ishan KarunaratneIshan Karunaratne⏱️ 12 min readUpdated
Full migration from MySQL 5.7 to 8.0: prerequisites, mysqlcheck dry-run, in-place upgrade, authentication-plugin caching_sha2_password switch, and rollback.

MySQL 5.7 reached end of life on October 31, 2023. Oracle stopped publishing security patches for it. Most managed cloud databases (RDS, Cloud SQL, Aurora) have pushed forced upgrades since then; self-hosted installations are on borrowed time. The migration to 8.0 is mostly straightforward but has three sharp edges that bite people: the default authentication plugin changed to caching_sha2_password and breaks every client that doesn't speak it, removed and reserved keywords cause stored procedures and views to fail validation, and the new data dictionary means there is no painless rollback once mysql_upgrade has rewritten system tables. Below is the full procedure I use, the verification commands at each step, and what to do when the auth plugin breaks PHP / Node.js / Python clients.

How do I migrate MySQL from 5.7 to 8.0?

The migration is a five-step procedure. First, take a full logical backup with mysqldump --all-databases --single-transaction --master-data=2. Second, run mysqlcheck --check-upgrade --all-databases against 5.7 to catch incompatibilities (reserved-word conflicts, deprecated character sets, partitioned tables on non-InnoDB engines). Third, stop the 5.7 service, install the 8.0 binaries on the same data directory (in-place upgrade), and start MySQL 8.0; the server runs mysql_upgrade automatically on first start since 8.0.16 and rewrites the system schema. Fourth, switch the default authentication plugin back to mysql_native_password if you have legacy clients, or update every client driver to one that speaks caching_sha2_password. Fifth, validate with a smoke-test query against each application before you delete the backup. Rollback after mysql_upgrade runs is not possible without restoring from the dump.

Jump to:

Why migrate (and the EOL clock)

MySQL 5.7 reached end of life on October 31, 2023. After that date, Oracle no longer publishes security patches, bug fixes, or even build artifacts on the public download mirrors. The CVE backlog for 5.7 since EOL is non-trivial and growing. If you run 5.7 in production today, you're carrying an audit risk that will only get worse.

MySQL 8.0 is the current GA branch with active support through April 2026 and extended support through April 2032. MySQL 8.4 LTS is the new long-term branch (see the 8.0 to 8.4 LTS upgrade once 8.0 is done). The 5.7 → 8.0 jump is the bigger, scarier one; 8.0 → 8.4 is a much smaller surface.

Pre-flight: backup everything

Before touching anything, take a full logical backup. The --single-transaction flag uses a consistent snapshot at the InnoDB level so the dump completes without locking tables on a live server:

bash
mysqldump \
    --all-databases \
    --single-transaction \
    --master-data=2 \
    --routines \
    --triggers \
    --events \
    --hex-blob \
    | gzip > mysql-5.7-pre-upgrade-$(date +%Y%m%d-%H%M).sql.gz

--single-transaction only works for InnoDB tables. If you still have MyISAM tables, convert them to InnoDB before migrating (here is why).

--master-data=2 writes the binary-log coordinates as a comment in the dump, useful for re-attaching replicas. --routines --triggers --events makes sure stored procedures, triggers, and scheduled events end up in the dump (they're skipped by default).

A logical backup is verbose but portable. For multi-terabyte databases, consider Percona XtraBackup for a physical backup; the rest of this guide assumes a logical dump because it works for every storage backend, and because it's the one rollback path that actually works post-upgrade.

Pre-flight: run the upgrade checker

MySQL ships an upgrade checker that scans your schema for known 8.0 incompatibilities without modifying anything:

bash
mysqlcheck --check-upgrade --all-databases

For more detail, the MySQL Shell upgrade checker (since 8.0.13) is more thorough:

bash
mysqlsh -- util check-for-server-upgrade '{"user":"root","host":"localhost","password":"YOUR_PASSWORD"}' \
    --target-version=8.0.42 \
    --output-format=JSON

The checker emits findings in three buckets:

SeverityWhat it means
ErrorWill break on 8.0. Must fix before upgrade.
WarningWill work but behavior changes. Review before upgrade.
NoticeInformational. Safe to ignore.

The most common errors I see in real codebases:

  • Reserved word collisions: 8.0 reserves GROUPS, ROLES, LATERAL, WINDOW, RANK, DENSE_RANK, RECURSIVE, and others. Any unquoted use as a column or table name fails.
  • Partitioned tables on non-InnoDB engines: 8.0 removes native MyISAM partitioning. Convert to InnoDB or remove partitioning.
  • utf8 vs utf8mb4: utf8 is still an alias for utf8mb3 in 8.0 but is deprecated and will be removed. Plan a utf8 to utf8mb4 migration on the same maintenance window if you haven't already.
  • ZEROFILL on numeric columns: deprecated in 5.7, still works in 8.0 but emits warnings.

Fix all errors, decide what to do with each warning, then re-run the checker until it's clean.

The in-place upgrade itself

On a self-hosted box with Debian / Ubuntu (the procedure is similar for RHEL / Rocky / Alma — substitute dnf for apt):

bash
# 1. Stop MySQL 5.7
sudo systemctl stop mysql

# 2. Replace the apt source to 8.0
sudo apt remove mysql-server-5.7 mysql-client-5.7
sudo apt install -y wget lsb-release gnupg
wget https://dev.mysql.com/get/mysql-apt-config_0.8.29-1_all.deb
sudo dpkg -i mysql-apt-config_0.8.29-1_all.deb
# (select MySQL 8.0 in the config dialog)
sudo apt update

# 3. Install the 8.0 server (keeps the existing /var/lib/mysql data directory)
sudo apt install -y mysql-server

# 4. Start it
sudo systemctl start mysql

On first start, MySQL 8.0 detects the older data directory and runs the upgrade automatically since mysql_upgrade was integrated into the server itself in 8.0.16. There is no longer a separate mysql_upgrade binary to run; on older 8.0.x point releases it's still there as a no-op for compatibility.

Watch the error log during first startup. If anything goes wrong it ends up here:

bash
sudo tail -f /var/log/mysql/error.log

The upgrade rewrites the system schema (the data dictionary moves from .frm files to a transactional table in InnoDB) and rebuilds the performance schema. On a few-gigabyte database this is seconds; on hundreds of gigabytes with many tables it can take ten minutes or more.

The authentication-plugin trap

This is the single biggest production incident waiting to happen. MySQL 8.0 changes the default authentication plugin from mysql_native_password to caching_sha2_password. Existing user accounts keep their old plugin, but any new account created on 8.0 (and any account whose password you change) ends up on the new plugin. Older client libraries that don't speak it fail with:

code
ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded

Two ways to handle it. Option A (preferred for new deployments): update every client to a driver that speaks the new plugin. The list as of 2026:

ClientVersion that supports caching_sha2_password
MySQL Connector/J (Java)8.0.9+
MySQL Connector/Python8.0.5+
mysqlclient (Python)2.0.0+
node-mysql22.0.0+ (always supported; mysql package never will)
PHP mysqlndPHP 7.2.8+ with PHP-MySQLnd compiled with the right plugin support
PHP mysqli / PDO_MySQLPHP 8.0+ has it natively
Go go-sql-driver/mysql1.5.0+
Ruby mysql2 gem0.5.3+

Option B (legacy-client compatibility): keep the default at mysql_native_password server-side, so newly-created users continue to land on the old plugin. Set in /etc/mysql/mysql.conf.d/mysqld.cnf:

ini
[mysqld]
default_authentication_plugin=mysql_native_password

Restart MySQL. From 8.4 this directive is removed and replaced with authentication_policy='mysql_native_password', so plan to switch to a properly-supported client before the next major upgrade.

For an existing user already on the new plugin who needs to revert:

sql
ALTER USER 'app_user'@'%'
    IDENTIFIED WITH mysql_native_password
    BY 'their_password';

Verify the upgrade succeeded

After the upgrade completes, verify:

sql
-- Server version
SELECT VERSION();
-- → 8.0.42 (or whichever 8.0.x you installed)

-- Data dictionary status (no orphaned tables)
SELECT * FROM information_schema.INNODB_TABLESPACES
WHERE NAME LIKE 'mysql/%' LIMIT 5;

-- User plugin distribution
SELECT plugin, COUNT(*) FROM mysql.user GROUP BY plugin;

-- Storage engine sanity
SELECT engine, COUNT(*) FROM information_schema.tables
WHERE table_schema NOT IN ('mysql','sys','information_schema','performance_schema')
GROUP BY engine;

Then run application smoke tests. The pattern I use: a small health-check.sh that runs one representative query against each of the top 5-10 tables and times it. Compare timings to a pre-upgrade baseline.

For a full cheat sheet of inspection commands for the post-upgrade verification phase, that article is the reference.

Removed and reserved keywords to watch

8.0 removed several keywords and reserved several new ones. The fast way to find offenders is the upgrade checker, but the high-impact ones in real codebases:

KeywordAction
GROUPSReserved. Quote with backticks: `GROUPS`
WINDOWReserved. Same fix.
ROLESReserved. Same fix.
LATERALReserved. Same fix.
RANK, DENSE_RANK, PERCENT_RANKReserved as window functions.
JSON_TABLEReserved.
RECURSIVEReserved (CTEs).
ASC / DESC as identifiers in CREATE INDEXNow actually means sort order, not just a column name.

Stored procedures, functions, triggers, and views that reference reserved words as unquoted identifiers will fail validation during the upgrade. Fix in the dump, restore, or fix in-place after the upgrade with ALTER.

Rollback options

Once MySQL 8.0 has started against your 5.7 data directory and rewritten the system schema, you cannot roll back to 5.7 by reinstalling the 5.7 binaries. The data dictionary is incompatible.

Your rollback is the logical backup you took in step one:

bash
# 1. Stop 8.0, uninstall the 8.0 binaries, install 5.7 again
sudo systemctl stop mysql
sudo apt remove --purge mysql-server-8.0
# (re-add the 5.7 apt source — Oracle has retired the public 5.7 mirrors,
#  so use the Percona Server 5.7 packages or a snapshot of an old archive.mirror)
sudo apt install -y mysql-server-5.7
sudo systemctl stop mysql

# 2. Wipe the 8.0-rewritten data directory
sudo rm -rf /var/lib/mysql

# 3. Re-init 5.7 and restore the backup
sudo mysqld --initialize-insecure --user=mysql
sudo systemctl start mysql
gunzip < mysql-5.7-pre-upgrade-*.sql.gz | mysql -u root

A logical restore on a multi-hundred-GB database takes hours. This is the reason the pre-flight checker matters: the only fast rollback is to not need one.

For the related "I locked myself out during the upgrade" scenario, how to reset a forgotten MySQL root password covers that.

What to do next

After 5.7 → 8.0 succeeds, the natural follow-ups:

External references:

FAQ

TagsMySQLMySQL 5.7MySQL 8.0MigrationUpgradeDatabase AdministrationAuthentication
Share
Ishan Karunaratne

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years across software, Linux systems, DevOps, and infrastructure — and a more recent focus on AI. Currently Chief Technology Officer at a tech startup in the healthcare space.

Keep reading

Related posts

Upgrade MySQL 8.0 to 8.4 LTS: removed deprecated options, authentication_policy variable, mysql_native_password disabled by default, in-place upgrade procedure.

How to Upgrade MySQL 8.0 to 8.4 LTS

MySQL 8.4 is the new LTS branch through April 2032. The 8.0 to 8.4 upgrade is much smaller than 5.7 to 8.0, but removed options and the new authentication_policy variable still bite. Full procedure with rollback.

Macro photograph of an old metal ruler being replaced by a longer brass measuring tape on a dark slate workbench, single warm side light

How to Change a MySQL Column Type

Change a MySQL column type with ALTER TABLE MODIFY or CHANGE. Covers data preservation, CAST behavior, INT to BIGINT, VARCHAR widening, and how ALGORITHM applies.