databases Azure

MySQL 8.4 LTS on Ubuntu 24.04 LTS | cloudimg

| Product: mysql-8-4-lts

MySQL 8.4 LTS on Ubuntu 24.04 LTS

MySQL 8.4 is the first long-term support (LTS) release in MySQL's new release cadence — stable through April 2032. This cloudimg image installs MySQL 8.4 Community Server from Oracle's official APT repository onto Ubuntu 24.04 LTS (Noble Numbat). A unique root password is generated automatically on first boot; no credential is baked into the image.

Prerequisites

  • An Azure subscription with permission to create virtual machines
  • An SSH key pair — the public key is uploaded during VM creation
  • MySQL client installed locally if you want to connect remotely

Step 1 — Deploy from the Azure Portal

  1. Find MySQL 8.4 LTS on Ubuntu 24.04 LTS by cloudimg in the Azure Marketplace and click Create.
  2. On the Basics tab, select or create a Resource Group, choose East US as the region, and set the VM size to Standard_D4s_v3 (4 vCPU / 16 GB RAM).
  3. Under Administrator account, choose SSH public key and paste your public key.
  4. On the Networking tab, open inbound port 3306 (MySQL) in addition to 22 (SSH). If you use the MySQL X Protocol, also open 33060.
  5. Click Review + create, then Create.

Step 2 — Deploy with the Azure CLI

az vm create \
  --resource-group <your-resource-group> \
  --name mysql-84-lts \
  --image "$(az sig image-version list \
       --resource-group AZURE-CLOUDIMG \
       --gallery-name cloudimgGallery \
       --gallery-image-definition mysql-8-4-lts-ubuntu-24-04 \
       --query '[0].id' -o tsv)" \
  --size Standard_D4s_v3 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard

After the VM starts, open the MySQL port:

az vm open-port --port 3306 --resource-group <your-resource-group> --name mysql-84-lts

Step 3 — Connect via SSH

ssh azureuser@<vm-ip>

On first boot, mysql-firstboot.service initialises the data directory and generates a unique root password. Allow 30–60 seconds for the service to complete before connecting.

Step 4 — Retrieve Your Credentials

sudo cat /stage/scripts/mysql-credentials.log

The file is owned root:root (mode 0600) and contains your root password, the socket path, and a ready-to-use connect command. Store the password somewhere safe before you rotate it.

Example output:

port=3306
socket=/var/run/mysqld/mysqld.sock
default_database=cloudimg
root_password=<generated-32-char-password>
connect_command=mysql -u root -p'<generated-32-char-password>' -h 127.0.0.1 -P 3306

Step 5 — Connect and Run a SQL Round-Trip

Verify the server version using the generated root password:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 --port=3306 \
  -e "SELECT VERSION();"

Create a table, insert a row, query it, then clean up:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 cloudimg <<SQL
CREATE TABLE IF NOT EXISTS greetings (
  id  INT PRIMARY KEY AUTO_INCREMENT,
  msg VARCHAR(128)
);
INSERT INTO greetings (msg) VALUES ('Hello from cloudimg');
SELECT * FROM greetings;
DROP TABLE greetings;
SQL

Step 6 — Server Components

Component Value
Package mysql-server 8.4.x from Oracle APT
Server binary /usr/sbin/mysqld
Client binary /usr/bin/mysql
Unix socket /var/run/mysqld/mysqld.sock
systemd unit mysql.service
First-boot unit mysql-firstboot.service
Service user mysql

Step 7 — Filesystem Layout

Path Purpose
/etc/mysql/mysql.conf.d/mysqld.cnf Main server configuration
/etc/mysql/my.cnf Active configuration (symlink)
/var/lib/mysql/ Data directory
/var/log/mysql/ Error log and slow-query log
/var/run/mysqld/ PID file and Unix socket
/stage/scripts/mysql-credentials.log Generated root password (mode 0600)

Step 8 — Configure Binary Logging for Replication

Binary logging is enabled by default in MySQL 8.4. Set a stable server_id and tune the format and expiry for production replication:

sudo tee -a /etc/mysql/mysql.conf.d/mysqld.cnf <<'EOF'

# Replication — set server_id uniquely per host in a replica set
[mysqld]
server_id                  = 1
binlog_format              = ROW
binlog_expire_logs_seconds = 604800
EOF
sudo systemctl restart mysql

Verify binary logging is active:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 \
  -e "SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'server_id';"

Step 9 — Set Up a Replica

On the source host, create a dedicated replication user:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 <<SQL
CREATE USER 'repl'@'%' IDENTIFIED WITH caching_sha2_password BY '<repl-password>';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
SQL

On the replica host (a second cloudimg MySQL 8.4 LTS image with server_id = 2), point it at the source:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 <<SQL
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='<source-private-ip>',
  SOURCE_USER='repl',
  SOURCE_PASSWORD='<repl-password>',
  SOURCE_AUTO_POSITION=1;
START REPLICA;
SHOW REPLICA STATUS\G
SQL

Step 10 — Configure TLS

MySQL 8.4 auto-generates self-signed TLS certificates at startup. Check the current TLS status:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 <<SQL
-- Show TLS variables
SHOW VARIABLES LIKE 'have_ssl';
SHOW VARIABLES LIKE 'ssl_cert';
-- To enforce TLS on a specific user account, run:
-- ALTER USER 'appuser'@'%' REQUIRE SSL;
-- FLUSH PRIVILEGES;
SHOW STATUS LIKE 'Ssl_cipher';
SQL

To supply your own CA and certificates, set ssl_ca, ssl_cert, and ssl_key in /etc/mysql/mysql.conf.d/mysqld.cnf and restart the service.

Step 11 — Tune innodb_buffer_pool_size

The InnoDB buffer pool is MySQL's most impactful memory setting. On a Standard_D4s_v3 (16 GB RAM), 12 GB is a reasonable starting point:

sudo tee -a /etc/mysql/mysql.conf.d/mysqld.cnf <<'EOF'

# InnoDB memory tuning
[mysqld]
innodb_buffer_pool_size      = 12G
innodb_buffer_pool_instances = 4
EOF
sudo systemctl restart mysql

Confirm the setting took effect:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 \
  -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"

Step 12 — Restrict Port 3306 and Rotate the Root Password

Create an application user with scoped privileges rather than using root for application connections:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 <<SQL
CREATE USER IF NOT EXISTS 'appuser'@'%'
  IDENTIFIED WITH caching_sha2_password BY 'ChangeThisPassword1!';
GRANT ALL PRIVILEGES ON cloudimg.* TO 'appuser'@'%';
FLUSH PRIVILEGES;
SQL

Bind MySQL to localhost once all connections go through a Unix socket or managed proxy:

sudo tee -a /etc/mysql/mysql.conf.d/mysqld.cnf <<'EOF'

# Restrict to localhost — remove the NSG port 3306 rule after applying this
[mysqld]
bind-address = 127.0.0.1
EOF
sudo systemctl restart mysql

Rotate the root password and update the credentials file:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
NEW_PASS=$(openssl rand -base64 32 | tr -d '/+=' | head -c 32)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 \
  -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY '${NEW_PASS}';"
printf 'root_password=%s\n' "${NEW_PASS}" | sudo tee /stage/scripts/mysql-credentials.log > /dev/null
sudo chmod 0600 /stage/scripts/mysql-credentials.log
sudo chown root:root /stage/scripts/mysql-credentials.log

Step 13 — Service Management

sudo systemctl status mysql
sudo systemctl restart mysql
sudo journalctl -u mysql -n 50 --no-pager

Troubleshooting

Service fails to start — inspect the error log and journal:

sudo tail -n 50 /var/log/mysql/error.log
sudo journalctl -u mysql --no-pager -n 50

Cannot connect on port 3306 — confirm MySQL is listening:

sudo ss -tlnp | grep 3306

caching_sha2_password handshake error — clients that do not cache the server's public key need the --get-server-public-key flag on first connection:

ROOT_PASS=$(sudo awk -F= '/^root_password=/{print $2}' /stage/scripts/mysql-credentials.log)
mysql -u root --password="${ROOT_PASS}" --host=127.0.0.1 \
  --get-server-public-key -e "SELECT 1;"

Security Recommendations

  • Rotate the root password immediately after your first login using the ALTER USER command shown in Step 12.
  • Create per-application users with the minimum required privileges (GRANT only the databases and operations the application needs).
  • If remote access is not required, set bind-address = 127.0.0.1 and remove the NSG rule for port 3306.
  • Require TLS on any user account that connects over a network (ALTER USER … REQUIRE SSL).
  • Keep the system patched: sudo apt-get update && sudo apt-get upgrade -y.

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 8.4 Reference Manual.