Deployment guide

MySQL phpMyAdmin

Videos

Overview: MySQL with phpMyAdmin on Ubuntu 24.04 LTS on Azure: overview (watch on YouTube)

Launch and log in: MySQL with phpMyAdmin on Ubuntu 24.04 LTS on Azure: launch and log in (watch on YouTube)

Deep Dive: MySQL with phpMyAdmin on Ubuntu 24.04 LTS on Azure: deep dive (watch on YouTube)

Overview

MySQL Community Server is the world's most widely deployed open-source relational database. The 8.4 series is a long-term support (LTS) release line. It receives security and bug-fix point releases without new features, so applications get a stable server for years.

phpMyAdmin is the long-established browser console for MySQL. You create and browse databases and tables, run SQL, import and export dumps, and manage user accounts and privileges without a desktop client.

This SecureAnyCloud image ships MySQL Community Server 8.4.13 from the official MySQL APT repository, together with phpMyAdmin 5.2.3, and adds the hardening and first-boot setup below.

What you get:

  • No shared password, anywhere. The image ships an empty MySQL data directory, and nothing in it was created on the build machine. On first boot, your VM initialises its own data directory and creates every secret: the dbadmin MySQL administrator password, the web sign-in password, phpMyAdmin's control user password and its cookie encryption secret. The two logins you need are written to /root/mysql-phpmyadmin-credentials.txt (root only).
  • No window without a password. The dbadmin administrator is created while MySQL runs with networking switched off, before MySQL first listens. If first boot cannot create it, MySQL, nginx and PHP-FPM stay stopped rather than starting without a credential.
  • Two logins, both unique to your VM. A browser sign-in prompt (HTTP Basic authentication in nginx) protects the whole phpMyAdmin site. phpMyAdmin's own login then signs you in as dbadmin, a full MySQL administrator. phpMyAdmin refuses root and blank passwords.
  • Root only from the VM. MySQL's root account has no password and no network access. It uses the auth_socket plugin and signs in only through the local socket as the operating system's root user (sudo mysql).
  • TLS everywhere, required. A MySQL TLS key and certificate are generated on your VM before MySQL first starts. Every TCP connection to MySQL must use TLS 1.2 or 1.3, and plain-text connections are refused. phpMyAdmin itself connects to MySQL over TCP with TLS and checks MySQL's certificate against the VM's own CA file. MySQL's automatic key generation is switched off, so no key is ever written into the data directory.
  • HTTPS from the first boot. nginx serves phpMyAdmin on port 443 with a certificate generated on your VM for its public address. Port 80 redirects to HTTPS, requests for a host name the VM does not serve are dropped, and phpMyAdmin's cookies are Secure, HttpOnly and SameSite=Strict.
  • No setup script on the web. phpMyAdmin's setup script, which lets whoever reaches it first write the configuration, is deleted along with its examples. nginx serves only phpMyAdmin's entry pages and static files. Its libraries, templates, Composer vendor/ tree, SQL scripts and configuration file answer 404. nginx allows 10 login attempts a minute from one address (HTTP 429 after that), and neither nginx nor PHP announces its version.
  • A hardened MySQL. MySQL listens on 127.0.0.1 only and the X Protocol port 33060 is off. There are no anonymous accounts and no test database. LOAD DATA LOCAL is off, server-side file access is confined to /var/lib/mysql-files, host name lookups are off and mysql_native_password is off. There is no binary or general query log.
  • Your data on its own disk. The data directory /var/lib/mysql is a dedicated 20 GiB Azure managed data disk that you can grow or snapshot on its own.
  • Signed, verified software. MySQL comes from the official MySQL 8.4 LTS APT repository, checked against the MySQL release signing key and pinned to that repository, so updates stay on the 8.4 long-term support line. phpMyAdmin is the current upstream release, checked against its published SHA-256 checksum and its OpenPGP signature by the phpMyAdmin release key (the Ubuntu package is the older 5.2.1).
  • Hardened defaults. MySQL, nginx, PHP-FPM and the first-boot services are blocked from the Azure Instance Metadata Service and the platform wire server. PHP is handed the configured host name and the real client address from the connection, never values a client sends.
  • Fully patched. Ubuntu 24.04 LTS is patched at build time, with unattended security upgrades switched on.
  • What is on the image: MySQL Community Server and client 8.4.13 (package 8.4.13-1ubuntu24.04 from the MySQL APT repository), phpMyAdmin 5.2.3, nginx, PHP 8.3 FPM, Ubuntu 24.04 LTS, cloud-init and the Azure Linux agent.
  • Defaults: the admin user is azureuser (or the name you choose), and Standard_B2s is the recommended starting size.

Prerequisites

  • An Azure subscription with permission to create virtual machines, and an SSH key pair (or a password) for the admin account.
  • A Network Security Group that allows TCP 22 (SSH) from your admin network, and TCP 443 (phpMyAdmin) and TCP 80 (redirects to HTTPS) from the networks your administrators work from.

Never open 3306 or 33060. Applications on other hosts reach MySQL through an SSH tunnel or a private network design of your own (Step 7).

1

Deploy from the Azure Portal

  • Find MySQL with phpMyAdmin on Ubuntu 24.04 LTS by SecureAnyCloud in the Azure Marketplace and click Create.
  • On the Basics tab, choose a resource group, region and size. Standard_B2s (2 vCPU, 4 GiB RAM) is a sensible starting point. Choose a larger size for bigger databases or heavier query loads.
  • Under Administrator account, use the username azureuser with an SSH public key (recommended) or a password. The image honours whichever you choose.
  • Under Inbound port rules, allow SSH (22), HTTP (80) and HTTPS (443). After the VM is created, restrict each rule to your own address ranges in the Network Security Group. Do not open 3306 or 33060.
  • On the Disks tab, review the image's 20 GiB data disk for /var/lib/mysql. Make it as large as your databases need.
  • Click Review + create, then Create. First boot takes well under a minute after the VM is running.
2

Deploy with the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:mysql-phpmyadmin-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-resource-group> \
  --name mysql-phpmyadmin \
  --image sac1647283583153:mysql-phpmyadmin-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard \
  --nsg-rule SSH

--nsg-rule SSH creates a Network Security Group that allows SSH only. Then allow the web console from your own address range only. Replace <your-cidr>, for example with 203.0.113.0/24:

az network nsg rule create --resource-group <your-resource-group> --nsg-name mysql-phpmyadminNSG \
  --name allow-phpmyadmin --priority 1010 --access Allow --protocol Tcp --direction Inbound \
  --source-address-prefixes <your-cidr> --destination-port-ranges 80 443

az vm create names the NSG <vm-name>NSG, so mysql-phpmyadminNSG for the example above. Check the name with az network nsg list -g <your-resource-group> -o table.

Do not run az vm open-port --port 3306. It opens MySQL to every address on the internet.

3

Connect and check the services

ssh azureuser@<vm-public-ip>

Connect over SSH. On first boot, the VM records its public address and creates the MySQL certificate, the data directory and the dbadmin administrator. It then creates phpMyAdmin's secrets and the web certificate, and starts nginx and PHP-FPM. Check the units:

systemctl is-active sac-public-ip mysql-tls-init mysql-datadir-init mysql mysql-phpmyadmin-firstboot php8.3-fpm nginx
sudo test -f /var/lib/sac/data/mysql-phpmyadmin-firstboot.done && echo FIRSTBOOT_DONE

All seven units are active, and the first-boot marker is present:

active active active active active active active FIRSTBOOT_DONE

Only SSH and nginx listen on network ports. MySQL listens on the loopback address only (no X Protocol port 33060), and PHP-FPM uses a local socket:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) 127.0.0.1:3306 users:(("mysqld")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx"))

The data directory is on the dedicated data disk (LUN 0):

findmnt -no FSTYPE,TARGET /var/lib/mysql df -h --output=size,target /var/lib/mysql
ext4 /var/lib/mysql Size Mounted on 20G /var/lib/mysql
4

Get your URL and passwords

Read the credentials file with sudo:

sudo cat /root/mysql-phpmyadmin-credentials.txt

Only root can read the file. It looks like this:

# MySQL with phpMyAdmin on Ubuntu 24.04 - generated on first boot (mysql-datadir-init + mysql-phpmyadmin-firstboot)
# These credentials are unique to this VM; none was baked into the image. Keep this file secret. PHPMYADMIN_URL=https://<vm-public-ip>/
# Step 1 - the browser's sign-in prompt (HTTP Basic gate in front of phpMyAdmin) WEB_GATE_USER=admin WEB_GATE_PASSWORD=<WEB_GATE_PASSWORD> # Step 2 - the phpMyAdmin login form (and the mysql client over TLS on the VM) MYSQL_ADMIN_USER=dbadmin MYSQL_ADMIN_PASSWORD=<MYSQL_ADMIN_PASSWORD> MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_TLS_CA=/etc/mysql/ssl/server-cert.pem # root is not accepted by phpMyAdmin and has no password: on the VM use sudo mysql (unix socket only). # Every TCP connection to MySQL must use TLS (require_secure_transport=ON); MySQL listens on 127.0.0.1:3306 only. # The web certificate is self signed (minted on this VM); port 80 redirects to https. # Change the name phpMyAdmin is served as:
sudo phpmyadmin-set-hostname <your-dns-name>
# The datadir /var/lib/mysql is the VM's dedicated Azure data disk. # User guide: https://secureanycloud.com/stacks/mysql-phpmyadmin/

The URL uses the VM's public IP address, which the boot service reads from the Azure metadata service on every boot. The web certificate also names the private address, so https://<vm-private-ip>/ works from inside your virtual network.

If the public address changes, for example a dynamic IP after a stop and start, the next boot picks up the new address and issues a new certificate for it. To serve phpMyAdmin under your own DNS name, see Step 8.

5

Sign in

Open the PHPMYADMIN_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time. You can install your own certificate instead (Step 8).

  • The browser shows its own sign-in prompt. Enter admin and the WEB_GATE_PASSWORD.
  • phpMyAdmin's login page opens. Sign in as dbadmin with the MYSQL_ADMIN_PASSWORD.

The home page shows the database server: MySQL 8.4.13 reached over TCP/IP on 127.0.0.1, with Server connection: SSL is used, signed in as dbadmin@127.0.0.1. It also shows the web server and the phpMyAdmin version, 5.2.3. The databases are listed on the left.

You can also check both logins from the VM. The self-test below runs two groups of checks.

First, it writes and reads a table as dbadmin over verified TLS, and shows that MySQL refuses a wrong password, a plain-text connection and root over the network.

Second, it signs in through the web gate and phpMyAdmin's real login form over HTTPS. It shows that no web gate password, or a wrong one, gets HTTP 401. It also shows that phpMyAdmin refuses a wrong dbadmin password, root and a blank password:

sudo mysql-phpmyadmin-cred-roundtrip.sh
OK dbadmin round-trips a table over verified TLS (wrong password, plaintext TCP and root over TCP refused; root only via the socket); the web gate refuses no/wrong credentials (401); dbadmin signs in to phpMyAdmin over HTTPS; wrong password, root and blank password refused
6

Work with your databases

Open Databases to list every database and create new ones. The phpmyadmin database holds phpMyAdmin's own settings, bookmarks and history, so leave it in place.

Click a table to browse, edit, insert and export rows.

The SQL tab runs any statement and shows the results. You can export the results, chart them or save them as a view.

Open User accounts to manage MySQL users and their privileges. Create a user per application with rights on its own database only, rather than using dbadmin from applications.

You can do the same from the command line on the VM. sudo mysql signs in as MySQL's root over the local socket, with no password and only from the VM:

sudo mysql -e "SELECT CURRENT_USER(), VERSION();"
sudo mysql -e "SELECT CONCAT(User,'@',Host) AS account, plugin, account_locked FROM mysql.user ORDER BY 1;"
CURRENT_USER() VERSION() root@localhost 8.4.13 account plugin account_locked dbadmin@127.0.0.1 caching_sha2_password N dbadmin@localhost caching_sha2_password N mysql.infoschema@localhost caching_sha2_password Y mysql.session@localhost caching_sha2_password Y mysql.sys@localhost caching_sha2_password Y pma@127.0.0.1 caching_sha2_password N root@localhost auth_socket N

root authenticates only through the socket, and MySQL's internal accounts are locked. dbadmin is the administrator, and pma is phpMyAdmin's control user, limited to the phpmyadmin database.

To work as dbadmin with the mysql client, keep its password in a client option file that only you can read, so it never appears on a command line. The command below writes ~/.mysql-dbadmin.cnf. The file points the client at the VM's own MySQL certificate and verifies the server's certificate and name:

( umask 077;
sudo awk -F= '/^MYSQL_ADMIN_PASSWORD=/{print "[client]\nuser=dbadmin\npassword=" $2 "\nhost=127.0.0.1\nssl-mode=VERIFY_IDENTITY\nssl-ca=/etc/mysql/ssl/server-cert.pem"}' /root/mysql-phpmyadmin-credentials.txt > ~/.mysql-dbadmin.cnf ) ls -l ~/.mysql-dbadmin.cnf | awk '{print $1}' mysql --defaults-file="$HOME/.mysql-dbadmin.cnf" -e "SELECT CURRENT_USER(); SHOW SESSION STATUS LIKE 'Ssl_version'; SHOW SESSION STATUS LIKE 'Ssl_cipher';"

The session is dbadmin over a verified TLS 1.3 connection:

-rw------- CURRENT_USER() dbadmin@127.0.0.1 Variable_name Value Ssl_version TLSv1.3 Variable_name Value Ssl_cipher TLS_AES_128_GCM_SHA256

Create a database and an application user, for example as below. Replace <new-password> with a strong password of your own. The statements go to MySQL on standard input:

sudo mysql <<'SQL' CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; CREATE USER 'appuser'@'localhost' IDENTIFIED BY '<new-password>'; CREATE USER 'appuser'@'127.0.0.1' IDENTIFIED BY '<new-password>'; GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost', 'appuser'@'127.0.0.1'; SQL

An application on the same VM connects to 127.0.0.1:3306 with TLS, or to the local socket /var/run/mysqld/mysqld.sock. Most MySQL drivers have an ssl-mode=REQUIRED or "require SSL" option. Give them /etc/mysql/ssl/server-cert.pem as the CA file to verify the server too.

New accounts use the caching_sha2_password plugin, which every current MySQL connector supports.

7

Connect from your own computer

MySQL is not exposed to the network. To use a desktop client from your computer (MySQL Workbench, DBeaver or the mysql command line), copy the VM's MySQL certificate so your client can verify the server. Then open an SSH tunnel and point the client at the local end of it.

Sign in as dbadmin, or as an application user created for 127.0.0.1. The certificate names 127.0.0.1, so verification works through the tunnel:

scp azureuser@<vm-public-ip>:/etc/mysql/ssl/server-cert.pem ./mysql-ca.pem ssh -N -L 3307:127.0.0.1:3306 azureuser@<vm-public-ip> mysql -h 127.0.0.1 -P 3307 -u dbadmin -p --ssl-mode=VERIFY_CA --ssl-ca=./mysql-ca.pem

Applications on other VMs have three options. Run them on this VM, tunnel the same way, or put MySQL behind your own private network design. For the private network option:

  • Change bind-address in /etc/mysql/mysql.conf.d/99-sac.cnf to the VM's private address.
  • Allow 3306 in the Network Security Group from those application hosts' address ranges only, never from Any or Internet.
  • Create accounts for their addresses with REQUIRE SSL.

Connections still have to use TLS, because require_secure_transport stays on.

8

Your own DNS name and certificates

Point a DNS record at the VM. A static public IP keeps the address stable. Then set the name that phpMyAdmin is served as.

The command below mints a new self-signed web certificate for the name and updates nginx and the URL in /root/mysql-phpmyadmin-credentials.txt. The name persists across reboots:

sudo phpmyadmin-set-hostname <your-domain>

To use a web certificate from a public or internal certificate authority instead, for example one issued by Let's Encrypt for <your-domain>, copy it over the generated pair and reload nginx. Running phpmyadmin-set-hostname again replaces it with a self-signed one:

sudo install -m 0644 <your-domain>.crt /etc/ssl/phpmyadmin/phpmyadmin.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/phpmyadmin/phpmyadmin.key
sudo nginx -t && sudo systemctl reload nginx

MySQL has its own certificate (/etc/mysql/ssl/). It names localhost, 127.0.0.1 and the VM's host name and private and public addresses.

If database clients reach MySQL by a DNS name of your own, re-issue the certificate with that name added. MySQL loads the new certificate at once (ALTER INSTANCE RELOAD TLS), and phpMyAdmin verifies it from then on, with no restart:

sudo mysql-tls-cert --renew db.example.internal
9

Security checks

Without the web gate login, the phpMyAdmin page answers 401. The setup script, phpMyAdmin's libraries, the Composer vendor/ tree, the configuration file and the SQL scripts are not served at all (404). Plain HTTP redirects to HTTPS:

H=$(sudo grep '^PHPMYADMIN_URL=' /root/mysql-phpmyadmin-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#') for p in /index.php /setup/ /libraries/classes/Version.php /vendor/autoload.php /config.inc.php /sql/create_tables.sql; do curl -sk -o /dev/null -w "%{http_code} $p\n" --resolve "$H:443:127.0.0.1" "https://$H$p" done curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' --resolve "$H:80:127.0.0.1" "http://$H/" ls /usr/share/phpmyadmin/setup 2>&1 | sed 's/^ls: //' [ "$(curl -sk -o /dev/null -w '%{http_code}' --resolve "$H:443:127.0.0.1" "https://$H/setup/")" = "404" ]
401 /index.php 404 /setup/ 404 /libraries/classes/Version.php 404 /vendor/autoload.php 404 /config.inc.php 404 /sql/create_tables.sql http:// -> 301 https://<vm-public-ip>/ cannot access '/usr/share/phpmyadmin/setup': No such file or directory

phpMyAdmin refuses root and password-less logins and can only reach the local server. It connects to MySQL over TCP with TLS, verifying MySQL's certificate against the VM's CA file.

Its configuration file holds no secret. The cookie encryption secret and the control user's password live in /etc/phpmyadmin/sac-secrets.ini, which is generated on first boot and readable by root and the web server only:

sudo grep -E "'(host|port|connect_type|ssl|ssl_ca|ssl_verify|AllowRoot|AllowNoPassword)'\]|AllowArbitraryServer'\]" /usr/share/phpmyadmin/config.inc.php
sudo stat -c '%A %U %G %n' /etc/phpmyadmin/sac-secrets.ini /usr/share/phpmyadmin/config.inc.php /etc/nginx/sac-phpmyadmin.htpasswd
$cfg['Servers'][$i]['host'] = '127.0.0.1'; $cfg['Servers'][$i]['port'] = '3306'; $cfg['Servers'][$i]['connect_type'] = 'tcp'; $cfg['Servers'][$i]['ssl'] = true; $cfg['Servers'][$i]['ssl_ca'] = '/etc/mysql/ssl/server-cert.pem'; $cfg['Servers'][$i]['ssl_verify'] = true; $cfg['Servers'][$i]['AllowRoot'] = false; $cfg['Servers'][$i]['AllowNoPassword'] = false; $cfg['AllowArbitraryServer'] = false; -rw-r----- root www-data /etc/phpmyadmin/sac-secrets.ini -rw-r----- root www-data /usr/share/phpmyadmin/config.inc.php -rw-r----- root www-data /etc/nginx/sac-phpmyadmin.htpasswd

MySQL runs with the SecureAnyCloud settings. It listens on loopback only, requires TLS, has LOAD DATA LOCAL and the binary log off, confines file access, and has the X Protocol and mysql_native_password plugins disabled:

sudo mysql -e "SELECT @@bind_address, @@require_secure_transport, @@tls_version, @@local_infile, @@log_bin, @@secure_file_priv;"
sudo mysql -e "SELECT PLUGIN_NAME, PLUGIN_STATUS FROM information_schema.PLUGINS WHERE PLUGIN_NAME IN ('mysqlx','mysql_native_password','auth_socket') ORDER BY 1;"
@@bind_address @@require_secure_transport @@tls_version @@local_infile @@log_bin @@secure_file_priv 127.0.0.1 1 TLSv1.2,TLSv1.3 0 0 /var/lib/mysql-files/ PLUGIN_NAME PLUGIN_STATUS auth_socket ACTIVE mysqlx DISABLED mysql_native_password DISABLED

A plain-text connection is refused, and root cannot sign in over the network with any password. The client also warns that a password on its command line is insecure, which is why Step 6 keeps the real one in a file:

mysql --defaults-file="$HOME/.mysql-dbadmin.cnf" --ssl-mode=DISABLED -e 'SELECT 1' 2>&1 || true
mysql --defaults-file="$HOME/.mysql-dbadmin.cnf" -u root --password=anything -e 'SELECT 1' 2>&1 || true
WARNING: no verification of server certificate will be done. Use --ssl-mode=VERIFY_CA or VERIFY_IDENTITY. ERROR 3159 (HY000): Connections using insecure transport are prohibited while --require_secure_transport=ON. mysql: [Warning] Using a password on the command line interface can be insecure. ERROR 1045 (28000): Access denied for user 'root'@'127.0.0.1' (using password: YES)

Both certificates were generated on this VM and name its own addresses. Only MySQL can read the MySQL key:

H=$(sudo grep '^PHPMYADMIN_URL=' /root/mysql-phpmyadmin-credentials.txt | cut -d= -f2- | sed -E 's#https://([^/]+)/#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName ls -l /etc/mysql/ssl | awk 'NR>1{print $1, $3, $4, $NF}'
subject=CN = <vm-public-ip>, O = sac X509v3 Subject Alternative Name: IP Address:<vm-public-ip>, IP Address:<vm-private-ip>, IP Address:127.0.0.1, DNS:localhost -rw-r--r-- root root server-cert.pem -rw------- mysql mysql server-key.pem

None of the services can reach the Azure Instance Metadata Service or the platform wire server. The Azure agent keeps its own access:

for u in mysql mysql-tls-init mysql-datadir-init mysql-phpmyadmin-firstboot nginx php8.3-fpm; do printf '%-28s %s\n' "$u" "$(systemctl show -p IPAddressDeny --value "$u" | tr ' ' '\n' | sort | paste -sd' ' -)"; done
mysql 168.63.129.16/32 169.254.169.254/32 mysql-tls-init 168.63.129.16/32 169.254.169.254/32 mysql-datadir-init 168.63.129.16/32 169.254.169.254/32 mysql-phpmyadmin-firstboot 168.63.129.16/32 169.254.169.254/32 nginx 168.63.129.16/32 169.254.169.254/32 php8.3-fpm 168.63.129.16/32 169.254.169.254/32

nginx hands PHP the configured host name and the client address from the connection, never values a client sends. It also keeps the web gate's credentials away from PHP:

grep -E 'fastcgi_param (HTTP_HOST|HTTPS|HTTP_X_FORWARDED_FOR|HTTP_X_REAL_IP|HTTP_AUTHORIZATION) ' /etc/nginx/sac-phpmyadmin-fastcgi.conf
fastcgi_param HTTP_HOST $pma_host; fastcgi_param HTTPS on; fastcgi_param HTTP_X_FORWARDED_FOR $remote_addr; fastcgi_param HTTP_X_REAL_IP $remote_addr; fastcgi_param HTTP_AUTHORIZATION "";

The packages come from the signed MySQL 8.4 LTS repository. phpMyAdmin is the verified upstream release:

cat /etc/sac/mysql-release.txt /etc/sac/phpmyadmin-release.txt apt-cache policy mysql-community-server | sed -n '1,3p'
sac: MySQL Community Server 8.4.13-1ubuntu24.04 (GPL-2.0-only) from the official MySQL 8.4 LTS APT repository (repo.mysql.com), signing key BCA43417C3B485DD128EC6D4B7B3B788A8D3785C. sac: phpMyAdmin 5.2.3 (GPL-2.0-only) from files.phpmyadmin.net, sha256 12ba1c425fa4071abbd4e7668c9ebdeac0b0755a467a6d6d5026122bb47c102b, OpenPGP signature by 3D06A59ECE730EB71B511C17CE752F178259BD92; setup/ and examples/ removed. mysql-community-server: Installed: 8.4.13-1ubuntu24.04 Candidate: 8.4.13-1ubuntu24.04
10

Changing passwords

  • dbadmin: in phpMyAdmin, choose Change password on the home page. This changes the dbadmin entry you signed in as, dbadmin@127.0.0.1. Alternatively, run the statements below for both dbadmin host entries (localhost and 127.0.0.1). Then update ~/.mysql-dbadmin.cnf, and update or delete MYSQL_ADMIN_PASSWORD in /root/mysql-phpmyadmin-credentials.txt.
sudo mysql <<'SQL' ALTER USER 'dbadmin'@'localhost' IDENTIFIED BY '<new-password>'; ALTER USER 'dbadmin'@'127.0.0.1' IDENTIFIED BY '<new-password>'; SQL
  • Web gate (admin): write a new hash into the nginx password file. The password stays in a shell variable, never on a command line. Then update or delete WEB_GATE_PASSWORD in the credentials file. nginx reads the file on every request, so no reload is needed:
NEWPW='<new-password>' printf 'admin:%s\n' "$(printf '%s\n' "$NEWPW" | openssl passwd -6 -stdin)" | sudo tee /etc/nginx/sac-phpmyadmin.htpasswd >/dev/null
sudo chown root:www-data /etc/nginx/sac-phpmyadmin.htpasswd && sudo chmod 0640 /etc/nginx/sac-phpmyadmin.htpasswd unset NEWPW
11

Backup and maintenance

Every database lives in /var/lib/mysql on the data disk. The simplest backup is an Azure snapshot of that managed disk, or Azure Backup for the whole VM. Take it with MySQL stopped for a consistent copy.

For a logical backup while MySQL is running, use mysqldump:

sudo mysqldump --all-databases --single-transaction --routines --triggers --events | gzip > <backup-dir>/mysql-$(date +%F).sql.gz

phpMyAdmin's Export and Import tabs do the same for single databases from the browser (uploads up to 256 MB).

The binary log is off, so point-in-time recovery is not available out of the box. To use replication or point-in-time recovery, remove disable_log_bin from /etc/mysql/mysql.conf.d/99-sac.cnf and restart MySQL. Keep the binary logs on the data disk with your backups.

To grow the data disk, stop (deallocate) the VM and increase the disk size in the portal (or with az disk update --size-gb). Then start the VM and grow the file system once with sudo resize2fs "$(findmnt -no SOURCE /var/lib/mysql)".

Ubuntu security updates install automatically. MySQL comes from the MySQL 8.4 LTS repository, so you apply its point releases (8.4.14 and later) when you choose, because a database upgrade restarts the server:

sudo apt-get update && sudo apt-get install --only-upgrade mysql-server mysql-community-server mysql-client

Run sudo apt-get update && sudo apt-get upgrade to apply everything else.

phpMyAdmin is installed from the upstream release. To upgrade it, replace /usr/share/phpmyadmin with a newer release. Keep config.inc.php, and delete the new release's setup/ and examples/ directories.

The default InnoDB buffer pool is 128 MB. On a Standard_B2s (4 GiB), a good starting point is about half the memory. Add innodb_buffer_pool_size = 2G to /etc/mysql/mysql.conf.d/99-sac.cnf and restart MySQL.

Component Path

MySQL data directory /var/lib/mysql (dedicated Azure data disk)

MySQL settings /etc/mysql/mysql.conf.d/99-sac.cnf

MySQL certificate and key /etc/mysql/ssl/server-cert.pem, /etc/mysql/ssl/server-key.pem

MySQL certificate helper /usr/local/sbin/mysql-tls-cert

MySQL repository and pin /etc/apt/sources.list.d/mysql.sources, /etc/apt/preferences.d/sac-mysql.pref

phpMyAdmin /usr/share/phpmyadmin

phpMyAdmin configuration /usr/share/phpmyadmin/config.inc.php (mode 0640, no secrets)

phpMyAdmin secrets /etc/phpmyadmin/sac-secrets.ini (mode 0640)

nginx site /etc/nginx/sites-available/sac-phpmyadmin

Web gate password file /etc/nginx/sac-phpmyadmin.htpasswd (mode 0640)

Web certificate and key /etc/ssl/phpmyadmin/

Boot services sac-public-ip.service, mysql-tls-init.service, mysql-datadir-init.service, mysql-phpmyadmin-firstboot.service

Host name helper /usr/local/sbin/phpmyadmin-set-hostname

Error log /var/log/mysql/error.log

Credentials /root/mysql-phpmyadmin-credentials.txt (mode 0600)

12

Security recommendations

  • Allow port 22 from your admin network only, and ports 80 and 443 only from the networks your administrators work from. Keep 3306 closed, or open it to your application hosts only (Step 7). Never open it to the internet.
  • Use your own DNS name and a certificate your browsers trust (Step 8).
  • Create a MySQL user per application with rights on its own database only, and keep dbadmin for administration.
  • Change both passwords if they may have been shared (Step 10).
  • Apply MySQL point releases, keep the operating system patched, and snapshot the data disk regularly.

Support and licensing

MySQL Community Server and phpMyAdmin are open source under the GNU General Public License version 2. There is no per-user or per-CPU licence fee. The SecureAnyCloud charge covers packaging, hardening, first-boot setup, security patching and support.

MySQL is a trademark of Oracle and/or its affiliates. SecureAnyCloud is not affiliated with or endorsed by Oracle or the phpMyAdmin project.

  • Email: sales@secureanycloud.com
  • Website: https://secureanycloud.com
  • Support: 24/7, with a 24-hour response SLA

Deploy on Azure

Launch MySQL with phpMyAdmin on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure