MySQL Router 8.4 on Ubuntu 24.04 LTS | cloudimg
MySQL Router 8.4 on Ubuntu 24.04 LTS
MySQL Router is Oracle's official lightweight connection proxy for MySQL. It presents applications with a single stable endpoint and transparently routes each connection to a healthy backend — the standard connection tier in front of a MySQL InnoDB Cluster or replica set.
This cloudimg image installs MySQL Router 8.4 from Oracle's official APT repository onto Ubuntu 24.04 LTS (Noble Numbat), together with a bundled MySQL 8.4 LTS Community server as a default backend, so the appliance routes live traffic the moment it boots. A unique MySQL root password and a unique application-user password are generated automatically on first boot — no credential is baked into the image.
Topology
The image ships a self-contained routing tier you can use immediately and re-point at your own database in production:
| Listener | Port | Bind | Routes to |
|---|---|---|---|
| Classic protocol, read/write | 6446 | 0.0.0.0 |
MySQL 127.0.0.1:3306 |
| Classic protocol, read-only | 6447 | 0.0.0.0 |
MySQL 127.0.0.1:3306 |
| X protocol, read/write | 6448 | 0.0.0.0 |
MySQL 127.0.0.1:33060 |
| X protocol, read-only | 6449 | 0.0.0.0 |
MySQL 127.0.0.1:33060 |
The bundled MySQL server is bound to loopback (127.0.0.1) and is reachable only through the router. MySQL authentication (a per-VM password, no anonymous login) is the security gate. In production you point the router at your own MySQL server or InnoDB Cluster — a configuration change, not a boot dependency.
Prerequisites
- An Azure subscription with permission to create virtual machines
- An SSH key pair — the public key is uploaded during VM creation
- A MySQL client installed locally if you want to connect from your workstation
Step 1 — Deploy from the Azure Portal
- Find MySQL Router 8.4 on Ubuntu 24.04 by cloudimg in the Azure Marketplace and click Create.
- On the Basics tab, select or create a Resource Group, choose East US as the region, and set the VM size to Standard_B2s (2 vCPU / 4 GB RAM) or larger.
- Under Administrator account, choose SSH public key and paste your public key.
- On the Networking tab, open the router ports you intend to use — 6446 (read/write) and 6447 (read-only) for the classic protocol, plus 6448/6449 for the X protocol — in addition to 22 (SSH). Restrict the source to your application subnet.
- Click Review + create, then Create.
Step 2 — Deploy with the Azure CLI
az vm create \
--resource-group <your-resource-group> \
--name mysql-router \
--image "$(az sig image-version list \
--resource-group AZURE-CLOUDIMG \
--gallery-name cloudimgGallery \
--gallery-image-definition mysql-router \
--query '[0].id' -o tsv)" \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_rsa.pub \
--public-ip-sku Standard
After the VM starts, open the router read/write port:
az vm open-port --port 6446 --resource-group <your-resource-group> --name mysql-router
Step 3 — Connect via SSH
ssh azureuser@<vm-ip>
On first boot, mysql-router-firstboot.service starts the backend MySQL server, generates the per-VM root and application passwords, creates the default cloudimg database and cloudimg_app user, and starts MySQL Router. Allow 30–60 seconds for the service to complete before connecting.
Step 4 — Retrieve Your Credentials
sudo cat /stage/scripts/mysql-router-credentials.log
The file is owned root:root (mode 0600) and lists the generated passwords, the router ports, and ready-to-use connect commands. Store the passwords somewhere safe before you rotate them.

Step 5 — Verify the Routing Topology
Confirm both services are healthy and inspect the listening ports — the router is on 0.0.0.0, the backend MySQL is loopback-only:
systemctl is-active mysql mysqlrouter
sudo ss -tlnp | grep -E ':(3306|33060|6446|6447|6448|6449) '

Step 6 — Connect Through the Router
Connect to the router read/write port (6446) using the generated application password, write a sentinel row, and read it back. The row records the backend port the connection actually reached — proving the traffic traversed the router:
APP_USER=$(sudo awk -F= '/^app_user=/{print $2}' /stage/scripts/mysql-router-credentials.log)
APP_PASS=$(sudo awk -F= '/^app_password=/{print $2}' /stage/scripts/mysql-router-credentials.log)
MYSQL_PWD="$APP_PASS" mysql -u "$APP_USER" -h 127.0.0.1 -P 6446 cloudimg --table <<'SQL'
CREATE TABLE IF NOT EXISTS demo (id INT PRIMARY KEY AUTO_INCREMENT, note VARCHAR(64), via_port INT, backend_port INT);
INSERT INTO demo (note, via_port, backend_port) VALUES ('hello via router', 6446, @@port);
SELECT note, via_port AS connected_to_router_port, backend_port AS mysql_server_port FROM demo ORDER BY id DESC LIMIT 1;
SQL
You connected to the router on 6446, but the backend reports its port as 3306 — the connection was routed through MySQL Router to the MySQL server behind it.

Step 7 — Use the Read-Only Port and Inspect the Router Log
Read the same data back over the router's read-only port (6447), then confirm the router logged that it is accepting connections on each routing port:
APP_USER=$(sudo awk -F= '/^app_user=/{print $2}' /stage/scripts/mysql-router-credentials.log)
APP_PASS=$(sudo awk -F= '/^app_password=/{print $2}' /stage/scripts/mysql-router-credentials.log)
MYSQL_PWD="$APP_PASS" mysql -u "$APP_USER" -h 127.0.0.1 -P 6447 cloudimg --table \
-e "SELECT note, via_port, backend_port FROM demo ORDER BY id DESC LIMIT 3;"
sudo grep 'Start accepting' /var/log/mysqlrouter/mysqlrouter.log | tail -4

Step 8 — Point the Router at Your Own MySQL or InnoDB Cluster
In production the router fronts your database. Edit the destinations in /etc/mysqlrouter/mysqlrouter.conf and restart the service. For a single server, change the destinations lines to your host:
sudo sed -i 's#destinations=127.0.0.1:3306#destinations=<your-mysql-host>:3306#' /etc/mysqlrouter/mysqlrouter.conf
sudo sed -i 's#destinations=127.0.0.1:33060#destinations=<your-mysql-host>:33060#' /etc/mysqlrouter/mysqlrouter.conf
sudo systemctl restart mysqlrouter
For a MySQL InnoDB Cluster, re-bootstrap the router against the cluster so it tracks membership and failover automatically (this rewrites mysqlrouter.conf with a metadata cache):
sudo mysqlrouter --bootstrap <cluster-admin>@<your-mysql-host>:3306 \
--user mysqlrouter --force
sudo systemctl restart mysqlrouter
Step 9 — Service Management
sudo systemctl status mysqlrouter --no-pager
sudo /usr/local/sbin/mysql-router-status.sh
sudo journalctl -u mysqlrouter -n 50 --no-pager
Step 10 — Security Recommendations
- Point the router at your own MySQL server or InnoDB Cluster (Step 8) and keep the bundled backend for evaluation only, or repurpose it as a production node.
- Open only the router ports your applications need (6446/6447 for classic, 6448/6449 for X protocol), and restrict the NSG source to your application subnet.
- Rotate the generated application password and create least-privilege users for each application:
ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-router-credentials.log)
mysql -u root --password="${ROOT_PASS}" --socket=/var/run/mysqld/mysqld.sock <<SQL
ALTER USER 'cloudimg_app'@'%' IDENTIFIED WITH caching_sha2_password BY '<new-password>';
FLUSH PRIVILEGES;
SQL
- Keep the system patched:
sudo apt-get update && sudo apt-get upgrade -y.
Troubleshooting
Router not accepting connections — confirm it is listening and check the log:
sudo ss -tlnp | grep 6446
sudo tail -n 50 /var/log/mysqlrouter/mysqlrouter.log
Backend unreachable — the router starts even when its destination is down and returns errors to clients until the backend is up. Confirm the backend MySQL server is running and reachable from the router host:
sudo systemctl status mysql --no-pager
Support
This image is provided and supported by cloudimg. For deployment questions, configuration assistance, or bug reports, contact us at support@cloudimg.co.uk.
For upstream documentation, see the MySQL Router 8.4 Reference.