guide·intermediate·updated 2026-10-21

RDS MySQL 8.0 to 8.4 Upgrade for Node.js Apps

Upgrade RDS MySQL 8.0 to 8.4 without breaking Node.js logins. Replace the legacy driver, migrate auth plugins, and cut over with Blue/Green. Tested on 8.0.

mysqlaws-rdsnodejsmysql2database-upgradeauthenticationblue-green-deploymentdevopsfounderscost-optimization

AWS moved RDS for MySQL 8.0 past standard support on July 31, 2026. Since then, instances still on 8.0 accrue Extended Support charges, billed per vCPU per hour, starting the day after that date. Multi-AZ standby instances are billed too. Every day you wait costs money, but the upgrade has a second risk that most database guides skip: 8.4 disables the mysql_native_password login method, and the legacy mysql npm driver can't connect to the caching_sha2_password accounts that replace it.

This guide covers the cost check, the auth and driver changes that break Node.js apps, a Blue/Green upgrade with a rollback path, and the settings to avoid. We ran the Node.js code and the authentication tests on MySQL 8.0.46 and mysql2 3.24.6. We could not run a MySQL 8.4 server in this environment, so the 8.4 behaviour below comes from the official MySQL 8.4 manual.

What you need: AWS CLI access, a MySQL client, Node.js 20 or later, and a staging copy of your database. Automated backups must be enabled on the source instance, since Blue/Green requires them.


Step 1: Check What the Upgrade Costs You

Extended Support billing has a simple formula:

monthly cost = vCPUs × hourly rate × 730 × (2 if Multi-AZ, else 1)

AWS charges per vCPU per hour and says the rate varies by Region. We could not read the MySQL 8.0 hourly rate from AWS's pricing page, so use your Region's rate from the RDS for MySQL pricing page. A third-party calculation for a 4-vCPU Single-AZ instance in us-east-1 gives $292/month for years 1–2 and $584/month for year 3. Confirm the current rate before you budget with it.

Two facts from AWS's Extended Support charges page matter here. Charges start the day after the end-of-standard-support date. And Multi-AZ standby instances are charged too.

Find your instances and their versions:

aws rds describe-db-instances \
  --query 'DBInstances[].{id:DBInstanceIdentifier,engine:EngineVersion,vcpu:ProcessorFeatures,multiAZ:MultiAZ}' \
  --output table

Step 2: Find the Accounts That Use Native Passwords

MySQL 8.4 disables mysql_native_password by default. According to the MySQL 8.4 manual, it's "disabled by default in MySQL 8.4" and can be re-enabled with --mysql-native-password=ON or mysql_native_password=ON in the [mysqld] section. Treat that as a temporary bridge, not a fix.

Save this as check-auth.js. It lists every account that still depends on the old plugin:

// check-auth.js: which app logins still depend on mysql_native_password?
// Run before upgrading. Needs SELECT on mysql.user.
const { pool } = require('./db');
 
(async () => {
  const [rows] = await pool.query(
    "SELECT user, host, plugin FROM mysql.user WHERE plugin = 'mysql_native_password' AND user NOT IN ('mysql.sys','mysql.session','mysql.infoschema')"
  );
  if (rows.length === 0) {
    console.log('OK: no accounts use mysql_native_password.');
  } else {
    console.log(`${rows.length} account(s) still use mysql_native_password:`);
    for (const r of rows) console.log(`  ${r.user}@${r.host}`);
  }
  await pool.end();
})().catch((e) => { console.error(e.code, e.message); process.exit(1); });

Also check the parameter group your instance uses. In the 8.4 manual, default_authentication_plugin is removed and replaced by authentication_policy. If your custom parameter group or my.cnf sets it, remove that line before you move to 8.4.


Step 3: Replace the Legacy mysql Driver

This is the step most upgrade guides miss. In our test on MySQL 8.0.46, the legacy mysql package connected to a mysql_native_password account but failed against a caching_sha2_password account:

mysql   legacy(native) : OK
mysql   modern(sha2)   : ERR ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client

The same mysql package also failed for a user we had migrated to caching_sha2_password. So you can't fix this just by changing users. Replace the driver first.

Install mysql2 and remove the old package:

npm uninstall mysql
npm install mysql2

Then use one shared pool in db.js. The pool replaces idle connections that were dropped. The single retry covers a query that was in flight when its connection dropped, such as during a switchover. We did not reproduce an in-flight drop, so treat the retry branch as untested until you exercise it in staging:

// db.js: shared MySQL pool using mysql2 (supports caching_sha2_password).
const mysql = require('mysql2/promise');
 
const pool = mysql.createPool({
  host: process.env.DB_HOST,
  port: Number(process.env.DB_PORT || 3306),
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  waitForConnections: true,
  connectionLimit: 10,
  enableKeepAlive: true,
  keepAliveInitialDelay: 10_000,
  maxIdle: 10,
  idleTimeout: 60_000,
});
 
// Retry once if the connection was dropped (for example during a Blue/Green switchover).
const RETRYABLE = new Set(['PROTOCOL_CONNECTION_LOST', 'ECONNRESET', 'EPIPE', 'ER_SERVER_SHUTDOWN']);
async function query(sql, params) {
  try {
    const [rows] = await pool.query(sql, params);
    return rows;
  } catch (err) {
    if (!RETRYABLE.has(err.code)) throw err;
    const [rows] = await pool.query(sql, params);
    return rows;
  }
}
 
module.exports = { pool, query };

Replace every mysql.createConnection or mysql.createPool call in your codebase with this module. Also check scripts, cron jobs and BI tools that connect with older clients. Any client that doesn't support caching_sha2_password will fail after the migration.


Step 4: Migrate Accounts to caching_sha2_password

Migrate one account at a time, starting in staging. The command below is what we ran on MySQL 8.0.46:

ALTER USER 'reporting'@'10.0.%' IDENTIFIED WITH caching_sha2_password BY 'use-a-new-strong-password';
SELECT user, host, plugin FROM mysql.user WHERE user = 'reporting';

Set the password again in the same statement. The plugin change needs it. Then rerun check-auth.js. We tested the detector both ways: it listed a native-password account before the change, and returned OK after.

Update the connection string for each application role, then run a smoke test as each one:

// app-smoke.js: confirm each app role can connect and read the server version.
const { query, pool } = require('./db');
 
(async () => {
  console.log(await query('SELECT CURRENT_USER() AS user, VERSION() AS version'));
  await pool.end();
})().catch((e) => { console.error(e.code, e.message); process.exit(1); });

Run it with each role's credentials. We ran it against 8.0.46 with a migrated account, and it returned the expected user and version.


Step 5: Run the Upgrade Precheck

AWS runs mandatory prechecks automatically during console or CLI upgrades. It logs failures to PrePatchCompatibility.log, which you can read with aws rds download-db-log-file-portion (AWS Database Blog). You can also run the MySQL upgrade checker yourself from MySQL Shell:

mysqlsh> util.checkForServerUpgrade()

The checker needs PROCESS and SELECT privileges. It includes checks for mysql_native_password and deprecated authentication settings (MySQL Shell docs). Fix everything it reports before you continue.


Step 6: Build an 8.4 Green Environment With Blue/Green

Blue/Green creates a copy of the instance, upgrades the copy, and keeps the original running until you switch. It requires automated backups. The AWS CLI command looks like this:

aws rds create-blue-green-deployment \
  --blue-green-deployment-name mysql-84-upgrade \
  --source arn:aws:rds:us-east-1:123456789012:db:prod-mysql \
  --target-engine-version 8.4.x \
  --target-db-parameter-group-name mysql84-app-params

Replace 8.4.x with a version listed by aws rds describe-db-engine-versions --engine mysql --engine-version 8.4. Use an 8.4 parameter group that doesn't set default_authentication_plugin.

Once the green environment is ready, run your smoke test and the auth check against its endpoint. Then switch over.


Step 7: Switch Over and Know Your Rollback Path

AWS says switchover "times have been reduced to under five seconds." During switchover, RDS "drops existing connections and blocks new connections," so your app has to reconnect. The pool in db.js replaces dropped idle connections, and its retry covers queries in flight at the drop. Test a switchover in staging while the app is under load, because we did not run one. Check that your app's error handling also treats connection drops as transient.

If something breaks after the switchover, AWS says you can "manually switch back to the blue environment" (AWS Database Blog). Keep the blue instance until you've watched the new environment for long enough to trust it. Deleting it stops that rollback path.

After the switchover, confirm the surcharge has stopped by checking the instance's engine version and Cost Explorer for the Extended Support line.


Step 8: Avoid the Auto-Upgrade Trap

AWS's Extended Support page warns about one setting. Disabling Extended Support on an instance that is already past its end-of-support date "automatically upgrades to the next supported major version." The page shows this command:

aws rds modify-db-instance \
  --db-instance-identifier my-db-instance \
  --engine-lifecycle-support open-source-rds-extended-support-disabled

Don't run this on a production instance until you've decided when the upgrade happens and tested the app against the result. For new restores, AWS also lets you set the same option at creation time so the restored instance doesn't enroll in Extended Support at all.


Common Mistakes

Changing users before replacing the driver. The legacy mysql package fails for every caching_sha2_password account. Replace the driver in staging first.

Keeping mysql_native_password=ON permanently. It's a temporary bridge. Keep it only while you migrate accounts.

Leaving default_authentication_plugin in the parameter group. It's removed in 8.4, so the upgrade fails. Remove it before you start.

Skipping the Blue/Green rollback window. Deleting blue at switchover removes your fastest way back.

Assuming Blue/Green is zero-downtime. It's a few seconds of dropped connections. Your app has to reconnect cleanly.

Using SHOW MASTER STATUS in monitoring scripts. The 8.4 release notes list it as removed, replaced by SHOW BINARY LOG STATUS. Update any replication or backup script that calls it.


What We Tested

Check Result on MySQL 8.0.46 What it means for 8.4
mysql legacy driver, native-password account Connects Native login is disabled by default on 8.4, so this account fails after upgrade unless migrated
mysql legacy driver, caching_sha2_password account Fails with ER_NOT_SUPPORTED_AUTH_MODE Replace the driver before migrating accounts
mysql2 driver, native-password account Connects Fails on 8.4 unless migrated or re-enabled
mysql2 driver, caching_sha2_password account Connects Works on 8.4 (the default)
check-auth.js before and after migration Lists the native account, then OK Use it as a gate before switchover
ALTER USER ... caching_sha2_password Migrates the account Same command on 8.4
Pool after killing an idle connection Pool replaced it, next query succeeded The in-flight retry branch in db.js was not exercised

Not tested here: the 8.4 server itself, Blue/Green, and the switchover. We couldn't run a MySQL 8.4 server or the RDS Blue/Green feature in this environment. The 8.4 claims come from the MySQL 8.4 manual and AWS documentation.


Sources and Further Reading

Got stuck, or want this shipped end-to-end for you? bitroot.club builds custom products for founders. →