MySQL 8.0 reached end of life in April 2026, which means no more security fixes from Oracle for the community edition. The supported target is MySQL 8.4, the current long-term support (LTS) release. The move is a direct in-place upgrade, but 8.4 removed a number of things that 8.0 still accepted. Most of them fail quietly until the day a user cannot log in or the server refuses to start. This guide lists what breaks, gives a query or command for finding each one in your own system, and walks through the upgrade.
The short version
- You can upgrade directly from MySQL 8.0 to 8.4 (the manual's example path is 8.0.37 to 8.4.x). An in-place upgrade is supported. If you are still on 5.7, go to 8.0 first.
- Run MySQL Shell's upgrade checker against the server before you do anything else.
- The usual breakages are accounts that use
mysql_native_password, removed settings inmy.cnf, old replication commands in scripts, and changed InnoDB defaults. - There is no
mysql_upgradestep. The 8.4 server upgrades the data dictionary and system tables itself when it starts.
Step 1: run the upgrade checker
MySQL's own checker finds many of the problems below automatically. It ships with MySQL Shell, so use the 8.4 version of the shell, because the checker can only target versions up to the shell's own release number:
mysqlsh -- util checkForServerUpgrade root@localhost:3306 \ --target-version=8.4.0 --output-format=JSON --config-path=/etc/mysql/my.cnf Pass --config-path, or the checks that read your configuration file will fail. The account needs the RELOAD, PROCESS and SELECT privileges. Relevant checks include removedSysVars, removedFunctions, pluginUsage and authMethodUsage. The sections below show how to look for the same things by hand, which is useful when you want to see exactly what is affected.
What breaks, and how to find it
1. Accounts that use mysql_native_password
In 8.4 the mysql_native_password plugin is still present but disabled by default. Release note wording: it "can be enabled by starting MySQL with the new --mysql-native-password=ON server option, or by adding mysql_native_password=ON to the [mysqld] section". Any account that still uses it cannot log in until you change the account or turn the plugin back on. Find them:
SELECT user, host, plugin FROM mysql.user WHERE plugin = 'mysql_native_password'; On my test server, an account created with IDENTIFIED WITH mysql_native_password appeared here, and the system accounts did not. Fix each one before the upgrade by moving it to the default plugin. You must supply the password again, because the stored hash cannot be converted:
ALTER USER 'app'@'%' IDENTIFIED WITH caching_sha2_password BY 'the-password'; I ran this on MySQL 8.0.46 and then connected from PHP 8.3 (PDO MySQL) with the same credentials, and the connection worked. A PHP app or Laravel's mysql connection needs no other change for this. If you cannot change an account yet, such as one used by an old client library, add mysql_native_password=ON under [mysqld] as a temporary measure and plan to remove it, because the plugin is deprecated.
2. Settings in my.cnf that no longer exist
Removed options cause an error at startup rather than being ignored. Search your config and any systemd or Docker arguments for these:
grep -rniE 'default[-_]authentication[-_]plugin|expire[-_]logs[-_]days|skip[-_]ssl|^ *ssl *=|have_ssl|skip[-_]host[-_]cache|innodb *=|master[-_]info|relay[-_]log[-_]info|transaction[-_]write[-_]set' /etc/mysql /etc/my.cnf* 2>/dev/null | Removed | Use instead |
|---|---|
default_authentication_plugin | authentication_policy |
expire_logs_days | binlog_expire_logs_seconds |
--ssl, --skip-ssl, --admin-ssl, have_ssl, have_openssl | --tls-version, --admin-tls-version |
--skip-host-cache | --host-cache-size=0 |
--innodb, --skip-innodb | Nothing; InnoDB is always on |
--master-info-repository, --relay-log-info-repository, --master-info-file, --relay-log-info-file, transaction_write_set_extraction | Remove them |
This is not the full list; the release notes for 8.4.0 have the rest. The expire_logs_days removal date is worded inconsistently in the manual, but the 8.4 manual states it is removed and gives binlog_expire_logs_seconds as the replacement.
3. Old replication commands in scripts and monitoring
The "master/slave" statements are gone in 8.4. Anything that runs them, such as failover scripts, cron jobs, backup tooling and monitoring queries, fails with a syntax error:
| Removed in 8.4 | Replacement |
|---|---|
START SLAVE / STOP SLAVE | START REPLICA / STOP REPLICA |
SHOW SLAVE STATUS | SHOW REPLICA STATUS |
SHOW SLAVE HOSTS | SHOW REPLICAS |
RESET SLAVE | RESET REPLICA |
CHANGE MASTER TO ... MASTER_HOST ... | CHANGE REPLICATION SOURCE TO ... SOURCE_HOST ... |
SHOW MASTER STATUS | SHOW BINARY LOG STATUS |
RESET MASTER | RESET BINARY LOG AND GTIDS |
PURGE MASTER LOGS, SHOW MASTER LOGS | PURGE BINARY LOGS, SHOW BINARY LOGS |
The options inside these statements are renamed as well, so MASTER_USER becomes SOURCE_USER and MASTER_LOG_FILE becomes SOURCE_LOG_FILE. The status counters Com_show_slave_status and Com_change_master are also removed (replaced by Com_show_replica_status and Com_change_replication_source), which matters if a dashboard reads them. Search your tooling:
grep -rniE 'start slave|stop slave|show slave|reset slave|change master|show master|reset master|purge master|master_(host|user|password|log_file|log_pos|auto_position)' /opt /usr/local/bin /etc/cron* 2>/dev/null 4. FLOAT or DOUBLE columns with AUTO_INCREMENT
8.4 no longer allows AUTO_INCREMENT on FLOAT or DOUBLE columns. The release notes say the upgrade requires altering such tables first. Find them:
SELECT table_schema, table_name, column_name, data_type FROM information_schema.columns WHERE extra LIKE '%auto_increment%' AND data_type IN ('float', 'double'); On my test server this returned the one table I had created with id FLOAT AUTO_INCREMENT. Change those columns to an integer type before upgrading.
5. Foreign keys that point at non-unique columns
A foreign key that references a non-unique or partial key is nonstandard. In 8.4 the new setting restrict_fk_on_non_standard_key is ON by default, so creating one is rejected. The manual says upgrades from 8.0 are still supported when such keys exist, and the server logs warnings naming them. The practical risk is later: a CREATE TABLE or ALTER TABLE in your migrations that adds this kind of key will fail. Find existing ones with this query, which compares each foreign key's referenced columns with the unique indexes on the parent table:
WITH fk AS ( SELECT CONSTRAINT_SCHEMA AS fk_schema, TABLE_NAME AS fk_table, CONSTRAINT_NAME AS fk_name, REFERENCED_TABLE_SCHEMA AS ref_schema, REFERENCED_TABLE_NAME AS ref_table, GROUP_CONCAT(REFERENCED_COLUMN_NAME ORDER BY ORDINAL_POSITION) AS ref_cols FROM information_schema.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME IS NOT NULL GROUP BY CONSTRAINT_SCHEMA, TABLE_NAME, CONSTRAINT_NAME, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME ), uq AS ( SELECT TABLE_SCHEMA AS t_schema, TABLE_NAME AS t_table, GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) AS cols FROM information_schema.STATISTICS WHERE NON_UNIQUE = 0 GROUP BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME ) SELECT fk.fk_schema, fk.fk_table, fk.fk_name, fk.ref_table, fk.ref_cols FROM fk LEFT JOIN uq ON uq.t_schema = fk.ref_schema AND uq.t_table = fk.ref_table AND uq.cols = fk.ref_cols WHERE uq.cols IS NULL; On my test server it flagged a foreign key pointing at a non-unique index and another pointing at only the first column of a two-column unique index. A foreign key to a primary key, and one to the full two-column unique index, were not flagged. Fix flagged keys by pointing them at a unique key, or set restrict_fk_on_non_standard_key=OFF if you must keep them.
6. Changed InnoDB defaults
If you never set these in my.cnf, 8.4 behaves differently from 8.0 without any error. These come from the 8.4 manual's table of differing defaults. The "8.0 value" column is what I read from a running MySQL 8.0.46 server, and it matched the manual:
| Variable | 8.0 default | 8.4 default |
|---|---|---|
innodb_adaptive_hash_index | ON | OFF |
innodb_change_buffering | all | none |
innodb_flush_method (Linux) | fsync | O_DIRECT if supported, otherwise fsync |
innodb_io_capacity | 200 | 10000 |
innodb_log_buffer_size | 16 MiB | 64 MiB |
innodb_purge_threads | 4 | 1 if 16 or fewer logical CPUs, otherwise 4 |
temptable_max_ram | 1 GiB | 3% of RAM, within 1 to 4 GiB |
temptable_max_mmap | 1 GiB | 0 (off) |
The manual lists more (for example innodb_buffer_pool_instances, innodb_doublewrite_files and innodb_page_cleaners). Before upgrading, record what your server runs today, then compare after:
SELECT @@innodb_adaptive_hash_index, @@innodb_change_buffering, @@innodb_flush_method, @@innodb_io_capacity, @@innodb_log_buffer_size, @@innodb_purge_threads, @@temptable_max_ram, @@temptable_max_mmap; If a workload depended on the old behaviour, such as the adaptive hash index for read-heavy lookups, set the value explicitly in my.cnf rather than relying on a default. Measure with your own workload instead of assuming either direction is faster.
7. Other removals worth a search
-
mysql_upgradeis removed. Scripts or Ansible roles that call it will fail. The server does that work at startup. -
mysqlpumpis removed, along withlz4_decompressandzlib_decompress. Usemysqldumpor MySQL Shell's dump utilities. -
FLUSH HOSTSis removed. UseTRUNCATE TABLE performance_schema.host_cacheormysqladmin flush-hosts. -
LOCK TABLES ... LOW_PRIORITY WRITEis now a syntax error. - The
SET_USER_IDprivilege is removed. Grants that mention it fail; useSET_ANY_DEFINERandALLOW_NONEXISTENT_DEFINER. - Keyring and FIDO plugins such as
keyring_file,keyring_encrypted_file,keyring_ociandauthentication_fidoare removed in favour of thecomponent_keyring_*components andauthentication_webauthn. If you use encrypted tablespaces, read the keyring notes before upgrading. - Weak TLS ciphers are no longer supported in
ssl_cipherand related settings.
Step 2: the in-place upgrade
The manual's procedure for package or binary installs, with my additions marked:
- Back up first (my addition; the manual's in-place steps do not include it). Take a logical dump or a physical backup and confirm you can restore it.
- If you use InnoDB XA transactions, run
XA RECOVER;and commit or roll back anything it lists. - If you normally run with
innodb_fast_shutdown=2, switch to a fast or slow shutdown:SET GLOBAL innodb_fast_shutdown = 0; -- slow shutdown (1 = fast) - Shut the server down cleanly:
mysqladmin -u root -p shutdown. - Install the 8.4 packages or binaries.
- Start MySQL 8.4 on the existing data directory. If you have encrypted InnoDB tablespaces, load the keyring with
--early-plugin-load.
On first start the server upgrades the data dictionary, the mysql system database, and the Performance Schema, INFORMATION_SCHEMA and sys schemas. The manual says that if the dictionary step fails, the server reverts its changes, and you should start the old server again, fix the cause and retry. Read the error log during the first start (my addition), and do not put the service back into rotation until it has finished.
After the upgrade
- Confirm the version:
SELECT VERSION(); - Re-run your account query and a connection from each application, including read replicas, cron jobs and reporting tools.
- Check replication with
SHOW REPLICA STATUS, using the new statement name. - Compare the variables you recorded earlier.
- Time zone tables are not upgraded by the process. If you load them with
mysql_tzinfo_to_sql, reload them as described in the time zone section of the manual.
Rollback
Plan to roll back by restoring your backup. The manual has a separate "Downgrading MySQL" section, which I did not review, so read it before relying on any downgrade path. Test the whole procedure on a copy of production data first, with the same config file.
Frequently asked questions
Can I upgrade from MySQL 8.0 directly to 8.4?
Yes. The manual lists "8.0.37 to 8.4.x LTS" as a supported path, with in-place upgrade, logical dump and load, or replication. From 5.7 you must go through 8.0 first.
Do I still need to run mysql_upgrade?
No. The utility was removed in 8.4.0. The server upgrades the data dictionary and system tables when it starts.
Will my Laravel or PHP app work with MySQL 8.4?
Generally yes, as long as the database user does not use mysql_native_password. I connected from PHP 8.3 with PDO MySQL to a caching_sha2_password account on 8.0 successfully. Check your migrations too, since a foreign key to a non-unique column will now be rejected.
Is MySQL 8.0 still safe to run?
Not for new deployments. It reached end of life in April 2026, so new vulnerabilities will not be patched in the community edition.
What about managed services like RDS or Aurora?
Their upgrade process and support dates are set by the provider. The incompatibilities in this article still apply to your accounts, configuration and SQL, so follow the provider's upgrade documentation as well.
Sources
- MySQL 8.4 Reference Manual: Upgrade Paths
- MySQL 8.4 Reference Manual: Upgrading Binary or Package-based Installations on Unix/Linux
- MySQL 8.4 Release Notes: Changes in MySQL 8.4.0
- MySQL 8.4 Reference Manual: What Is New in MySQL 8.4 since MySQL 8.0
- MySQL Shell 8.4: Upgrade Checker Utility
- Atlas: MySQL 8 end of life
Originally published on DEV Talk.
