Databases Azure

MySQL 9 Community Server on Oracle Linux 10 on Azure User Guide

| Product: MySQL 9 Community Server on Oracle Linux 10 on Azure

Overview

This guide covers the deployment and configuration of MySQL 9 Community Server (version 9.6.0, Innovation track) on Oracle Linux 10 on Microsoft Azure using cloudimg's pre configured virtual machine image from the Azure Marketplace. MySQL 9 brings new SQL features, vector data types for AI workloads, and continuing support for the InnoDB storage engine. cloudimg ships MySQL 9 pre installed with production default configuration and a firstboot service that rotates the root password to a per VM unique value on first customer boot.

What's included in this VM image:

  • MySQL 9.6.0 Community Server from the official Oracle MySQL Innovation channel for Oracle Linux 10
  • Production default configuration at /etc/my.cnf.d/cloudimg.cnf (utf8mb4 default, 512M InnoDB buffer pool, slow query log on, max_connections 200)
  • Per VM root password rotation via mysql-firstboot.service on first boot
  • A default cloudimg database (utf8mb4_0900_ai_ci) ready for customer use
  • mysqld.service enabled for boot start
  • firewalld rule for TCP 3306 already opened on the OS
  • Logs at /var/log/mysql/error.log and /var/log/mysql/slow.log
  • Latest security patches applied at build time

Platform: Microsoft Azure (Gen2 Hyper V) Default OS user: azureuser

Step 1: Deploy the Virtual Machine

az vm create -g <resource-group> -n mysql-9-vm -l eastus \
  --image cloudimg:mysql-9-oel10:basic:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub

For higher load workloads, choose a memory optimised SKU such as Standard_E4s_v5 (4 vCPU, 32 GB RAM) and tune innodb_buffer_pool_size after first boot — see Step 12.

Step 2: Connect via SSH

ssh azureuser@<public-ip>

Confirm the OS:

cat /etc/oracle-release

Step 3: Confirm MySQL is Running

mysqld.service starts automatically at boot.

sudo systemctl is-active mysqld.service

Show full status:

sudo systemctl status mysqld.service --no-pager | head -12

Step 4: Locate the Per VM Root Password

The mysql-firstboot.service rotates the root password on first boot and writes the new credentials to /home/azureuser/CREDENTIALS.txt.

sudo cat /home/azureuser/CREDENTIALS.txt

The file is owned by root:root with mode 0600 and contains the per VM password, default database name, and the path to your config file. Copy the password to a secure location and remove the file when you're done.

Step 5: Confirm MySQL is Listening

sudo ss -tlnp | grep ':3306'

Step 6: Connect with the MySQL Client

mysql -uroot -p

Run a quick smoke test:

sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') -e "SELECT VERSION(), NOW()"

Step 7: Create an Application Database and User

The default cloudimg database is fine for testing, but production workloads should use a dedicated database and a least privilege user rather than connecting as root. Set the password to a strong value of your own choice:

sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') <<'SQL'
CREATE DATABASE IF NOT EXISTS appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
CREATE USER IF NOT EXISTS 'appuser'@'%' IDENTIFIED BY 'CHANGE_ME_StrongPasswordExample123!';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'%';
FLUSH PRIVILEGES;
SHOW GRANTS FOR 'appuser'@'%';
SQL

For a localhost only user (more secure if your application runs on the same VM), replace 'appuser'@'%' with 'appuser'@'localhost'.

Step 8: Open Azure NSG for Port 3306 (Remote Connections)

The OS firewalld already permits 3306, but the Azure Network Security Group attached to the VM blocks it by default. Open it from your application VNet only — never from 0.0.0.0/0 for production:

az network nsg rule create -g <resource-group> --nsg-name <nsg-name> \
  -n allow-mysql-3306 \
  --priority 200 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --source-address-prefixes 10.0.0.0/16 \
  --destination-port-ranges 3306

Replace 10.0.0.0/16 with your application subnet's CIDR. Test from a client machine on that VNet:

mysql -h <vm-private-ip> -P 3306 -u appuser -p

Step 9: Backup and Restore

Create a logical dump of a single database:

sudo mysqldump -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') \
  --single-transaction --routines --triggers --events \
  appdb > /home/azureuser/appdb-$(date +%Y%m%d).sql

Or back up everything (use --all-databases for a full server snapshot):

sudo mysqldump -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') \
  --all-databases --single-transaction --routines --triggers --events \
  --master-data=2 > /home/azureuser/full-$(date +%Y%m%d).sql

Restore a dump into a target database:

sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') appdb < /home/azureuser/appdb-20260509.sql

For routine production backups, schedule the dump via cron and copy it to Azure Blob Storage or an Azure Files share.

Step 10: Tuning InnoDB Buffer Pool for Larger VMs

cloudimg defaults innodb_buffer_pool_size to 512 MB so MySQL fits comfortably on the Standard_B2s build target (4 GB RAM total). On a larger VM, allocate roughly 50 to 70 percent of system RAM to the buffer pool. For example, on a Standard_E4s_v5 (32 GB RAM):

Add a customer override file at /etc/my.cnf.d/local.cnf — it loads after cloudimg.cnf and wins. Replace <buffer-pool-size> with a value appropriate to your VM RAM (e.g. 16G on Standard_E4s_v5):

sudo tee /etc/my.cnf.d/local.cnf >/dev/null <<CNF
[mysqld]
innodb_buffer_pool_size = <buffer-pool-size>
innodb_buffer_pool_instances = 8
innodb_log_file_size = 1G
max_connections = 500
CNF
sudo systemctl restart mysqld.service

Confirm the new value is applied:

sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'"

The local override survives package upgrades (cloudimg.cnf does not regenerate on package upgrade, but if you need to change shipped defaults, always do it in local.cnf rather than editing cloudimg.cnf in place).

Step 11: Slow Query Analysis

cloudimg enables the slow query log out of the box at /var/log/mysql/slow.log with a 2 second threshold. Generate some load and inspect:

sudo tail -30 /var/log/mysql/slow.log 2>/dev/null || echo "no slow queries logged yet"

Use the bundled mysqldumpslow to summarise the top offenders by total time:

sudo mysqldumpslow -s t /var/log/mysql/slow.log | head -30

To temporarily lower the threshold for debugging (no restart needed):

sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') -e "SET GLOBAL long_query_time = 0.5"

Set it back via SET GLOBAL long_query_time = 2 when you're done.

Step 12: SSL/TLS Connections

MySQL 9 generates self signed SSL certificates automatically on first init. Verify they're in place and check the server is willing to talk TLS:

sudo ls -la /var/lib/mysql/*.pem
sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') -e "SHOW VARIABLES LIKE 'have_ssl'"

Force a single user to connect over TLS only:

sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') -e "ALTER USER 'appuser'@'%' REQUIRE SSL"

For production, replace the auto generated certificates with ones from Azure Key Vault or Let's Encrypt and point ssl-cert, ssl-key, ssl-ca in /etc/my.cnf.d/local.cnf at the new files.

Managing the Server

Restart MySQL:

sudo systemctl restart mysqld.service

Tail the error log:

sudo tail -20 /var/log/mysql/error.log

Check open connections:

sudo mysql -uroot -p$(grep '^Password:' /home/azureuser/CREDENTIALS.txt | awk '{print $2}') -e "SHOW PROCESSLIST"

Updating MySQL Within the 9.x Innovation Channel

The image ships with mysql-innovation-community enabled and mysql-8.4-lts-community disabled. Apply minor 9.x updates as Oracle ships them with dnf upgrade:

sudo dnf upgrade -y --nogpgcheck mysql-community-server mysql-community-client
sudo systemctl restart mysqld.service
mysqld --version

Note: the MySQL signing key (B7B3B788A8D3785C) expired 2025-10-22 but Oracle continues to sign 9.x RPMs with it. The --nogpgcheck flag is required until Oracle publishes a 2026 replacement key. On dnf5 you may also need dnf download --resolve plus rpm -ivh --nosignature --nodigest.

Common Errors

"Access denied for user 'root'@'localhost' (using password: NO)" — firstboot has rotated the password. Re read /home/azureuser/CREDENTIALS.txt.

"Can't connect to MySQL server on '\<vm-ip>:3306'" — Azure NSG is blocking the port. See Step 8.

"Too many connections" — raise max_connections in /etc/my.cnf.d/local.cnf and restart.

"[Warning] Using a password on the command line interface can be insecure" — MySQL emits this on every command line password use. For automation, put the password in ~/.my.cnf mode 0600 and call mysql --defaults-file=~/.my.cnf instead.

"InnoDB: Cannot allocate memory for the buffer pool" — your VM doesn't have enough RAM for the configured innodb_buffer_pool_size. Lower it in /etc/my.cnf.d/local.cnf, or move to a larger SKU.

On Startup

mysqld.service is enabled at boot via systemd:

sudo systemctl is-enabled mysqld.service

mysql-firstboot.service runs once on the very first customer boot to rotate the root password, then stays in active (exited) state and is a no op on subsequent reboots.

Support

cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.

License: MySQL Community Server is licensed under GPLv2. cloudimg charges a per vCPU per hour fee for packaging, security patching, and 24/7 support.