As of May 2025 - MySQL 5.7 is now in extended support for CloudSQL meaning your cloud bill is about to ⬆️⬆️⬆️
This is how I upgraded our CloudSQL instance(s) from MySQL 5.7 - MySQL 8.0 with minimal downtime via an inplace migration.
⚠️ Always clone your instance and complete these steps fully before performing on your production instance
Let's get started
Prerequisites:
- Install MySQL Client - Download Link for MySQL Workbench
- Install MySQL Shell - Download Link
Remember to add to your PATH variable so you can run the following commands:
mysql --version
mysqlsh --version
Connect to Instance From Cloud Shell
🔗 Google Docs: Connect Instance Cloud Shell
Set the root user password
>gcloud sql users set-password root
--host=%
--instance=<instance_id>
--prompt-for-password
Then connect to the instance to check it worked:
> gcloud sql connect <instance_id> --user=root
Allowlisting your IP for incoming connection for 5 minutes...done.
You can also now run status
command to see the current version info
Now connect to mysqlsh
for the purpose of saving password (otherwise you need to enter password many times when running the below utility checker script):
>mysqlsh root@<ip>
Please provide the password for 'root@<ip>': *********************
Save password for 'root@<ip>'? [Y]es/[N]o/Ne[v]er (default No): Y
MySQL Shell 9.3.0
Run the MySQL Upgrade Utility Checker
MySQL provides an Upgrade Utility Checker
Running as reccomended results in a timeout, instead use the following shell script which runs each check at a time, and saves to a file.
#!/bin/bash
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Prompt for IP address
echo -n "Enter MySQL server IP: "
read ip
# Create save directory
save_path="$HOME/Desktop/mysql-upgrade-checks"
mkdir -p "$save_path"
echo -e "\n${CYAN}Fetching available checks from $ip...${NC}"
# Script to list checks
script_to_list_checks='var checks = util.checkForServerUpgrade(null, { list: true }); print(JSON.stringify(checks));'
# Get list of checks
raw_checks=$(mysqlsh --js --uri root@$ip --execute="$script_to_list_checks" 2>&1)
# Parse the check names from the output
# Extract check IDs from the "- checkName" lines in the "Included:" section
checks=$(echo "$raw_checks" | awk '
/^Included:$/ { in_included = 1; next }
/^Excluded:$/ { in_included = 0; next }
in_included && /^- / {
# Extract the check name (first word after "- ")
gsub(/^- /, "")
print $1
}
')
if [ -z "$checks" ]; then
echo -e "${RED}Error: Could not find any checks in the response.${NC}"
echo "Raw output: $raw_checks"
exit 1
fi
# Count checks
check_count=$(echo "$checks" | wc -l)
echo -e "${GREEN}Found $check_count checks.${NC}"
# Script template with placeholder
script_template='var result = util.checkForServerUpgrade(null, {
targetVersion: "8.0",
outputFormat: "JSON",
include: ["__CHECK_ID__"]
});
print(JSON.stringify(result, null, 2));'
# Process each check
echo "$checks" | while read -r check_id; do
if [ -n "$check_id" ]; then
output_file="$save_path/$check_id.json"
echo -e "${YELLOW}Running check: $check_id ...${NC}"
# Replace placeholder with actual checkId
script=$(echo "$script_template" | sed "s/__CHECK_ID__/$check_id/g")
# Run the check and save output, filtering out "undefined"
mysqlsh --js --uri root@$ip --execute="$script" 2>&1 | grep -v "^undefined$" > "$output_file"
if [ -f "$output_file" ]; then
echo -e "${GREEN}✔ Saved: $output_file${NC}"
else
echo -e "${RED}⚠ Failed to save output for $check_id${NC}"
fi
fi
done
echo -e "\n${CYAN}✅ All checks completed. Results saved to: $save_path${NC}"
You will need the public IP address of the instance which you can find from Google Cloud Console:
Additionally, make sure you are IP whitelisted for your CloudSQL instance, otherwise connection will be lost part-way through the checks if > 5 minutes.
Review the Upgrade Utility Checker Results
As per the above script, the results will be saved to JSON files in the specified location $HOME/Desktop/mysql-upgrade-checks
.
For this step internally, I created a python file to parse the JSON files into a condensed easy-to-read report. Below is a snippet of this outputted report, I highly suggest writing a script to produce a similar output for review:
=============
MySQL UPGRADE COMPATIBILITY CHECK REPORT
=============
SERVER INFORMATION
----------------------------------------
Server Address: <ip:
Current Version: 5.7.44-google-log - (Google)
Target Version: 8.0.42
EXECUTIVE SUMMARY
----------------------------------------
Total Files Processed: 37
Total Errors: 92
Total Warnings: 3093
Total Notices: 14259
Checks with Issues: 8
Checks OK: 23
❌ UPGRADE BLOCKED: Critical errors detected that must be resolved before upgrade.
CHECK SUMMARY ANALYSIS
=============================
🚨 CRITICAL ERRORS (Must Fix) (1 checks)
--------------------------------------------------
Check: MySQL syntax check for routine-like objects
Source: syntax.json
Affects 1 unique objects
Affects 92 databases/schemas
Total occurrences: 92
Sample objects: example_proc_name (Routine)
⚠️ WARNINGS (Should Address) (5 checks)
--------------------------------------------------
...
ℹ️ NOTICES (Informational) (1 checks)
--------------------------------------------------
...
CHECK-BY-CHECK SUMMARY
============================
...
Review this report and fix any critical issues accordingly. This may involve running ALTER statements to fix certain columns, or cleaning up unused legacy databases. When fixed, re-run the utility checker until you confirm there are no upgrade conflicts.
Take Backup of Instance
Note: that this is extra, as CloudSQL also takes automatic backups both pre & post upgrade
gcloud sql backups create --async --instance=<instance_id> --description=pre-mysql8-upgrade
Manually created backups are not deleted automatically, unless that instance is deleted. Google Docs Link
View the backup to confirm it was successfully made:
>gcloud sql backups list --instance <instance_id>
ID WINDOW_START_TIME ERROR STATUS INSTANCE
<backup_id> 2025-07-04T06:25:41.618+00:00 - SUCCESSFUL <instance_id>
<backup_id> 2025-07-04T04:05:16.766+00:00 - SUCCESSFUL <instance_id>
or view more detail about specific backup like:
>gcloud sql backups describe <backup_id> --instance <instance_id>
backupKind: SNAPSHOT
databaseVersion: MYSQL_5_7
description: pre-mysql8-upgrade
endTime: '2025-07-04T06:27:12.911Z'
enqueuedTime: '2025-07-04T06:25:41.618Z'
id: '<backup_id>'
instance: <instance_id>
kind: sql#backupRun
location: <location>
maxChargeableBytes: '<bytes>'
selfLink: https://sqladmin.googleapis.com/sql/v1beta4/projects/<project_id>/instances/<instance_id>/backupRuns/<backup_id>
startTime: '2025-07-04T06:25:41.627Z'
status: SUCCESSFUL
type: ON_DEMAND
windowStartTime: '2025-07-04T06:25:41.618Z'
Run Upgrade (Will cause some downtime!)
Get the database version for upgrade by running:
gcloud sql instances describe <instance_id> --format="table(upgradableDatabaseVersions)"
Choose the database version that you ran the MySQL utility checker against. You can see it at the top of the summary reports.
MYSQL_8_0_42
gcloud sql instances patch <instance_id> --database-version=<DATABASE_VERSION>
Depending on the size of your instance, this make take some time, up to 1 hour. The database will only be offline during a portion of this window.
You may get a timeout error which is fine, just run the following:
ERROR: (gcloud.sql.instances.patch) Operation https://sqladmin.googleapis.com/sql/v1beta4/projects/<project_id>/operations/<operation_id> is taking longer than expected. You can continue waiting for the operation by running `gcloud beta sql operations wait --project <project_id> <operation_id>`
List operations against the instance
>gcloud sql operations list --instance=<instance_id> --filter=STATUS=RUNNING
NAME TYPE START END ERROR STATUS
<operation_id> UPDATE 2025-07-04T06:41:23.812+00:00 T - RUNNING
Use the operation ID to monitor the status
In the event of errors! Revert to backup
gcloud sql backups restore <backup_id> --resotre-instance=<instance_id>
Complete the upgrade
See CloudSQL upgrade guide for recomendations about additional needed testing i.e. updating user privileges. This will differ based on your instance configuration.
The main change needed will be to update your database user permissions.
GRANT ALL PRIVILEGES
no longer works, so instead do this, tweaking based on which permissions you wish to grant to the user
GRANT
ALTER,
ALTER ROUTINE,
CREATE,
CREATE ROUTINE,
CREATE TEMPORARY TABLES,
CREATE VIEW,
DELETE,
DROP,
EVENT,
EXECUTE,
INDEX,
INSERT,
LOCK TABLES,
PROCESS,
REFERENCES,
SELECT,
SHOW DATABASES,
SHOW VIEW,
TRIGGER,
UPDATE
ON *.* TO `user`@`%`;
Finally, delete backups
After a period of time, if all is well, you can delete the manually created backup via:
>gcloud beta sql backups delete <backup_id> --instance=<instance_id>
You can also use the same method to delete the CloudSQL automatic backups, else they will persistent indefinitely.
Upgrade Complete!
Just like that, no more extended support bills to pay to Google Cloud :)
Top comments (0)