MySQL 8.4 LTS on Ubuntu 24.04 LTS | cloudimg
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
- Find MySQL 8.4 LTS on Ubuntu 24.04 LTS 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_D4s_v3 (4 vCPU / 16 GB RAM).
- Under Administrator account, choose SSH public key and paste your public key.
- On the Networking tab, open inbound port 3306 (MySQL) in addition to 22 (SSH). If you use the MySQL X Protocol, also open 33060.
- 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 USERcommand shown in Step 12. - Create per-application users with the minimum required privileges (
GRANTonly the databases and operations the application needs). - If remote access is not required, set
bind-address = 127.0.0.1and 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.