AlmaLinux

Install MySQL on Rocky Linux 10 / AlmaLinux 10 (9.7 or 8.4 LTS)

A plain dnf install mysql-community-server on Rocky Linux 10 fails with about 40 file conflicts against MariaDB packages you never asked for. The MySQL server package recommends a mysql system user, and DNF satisfies that weak dependency by pulling mariadb11.8-server from AppStream, which then collides with the MySQL client binaries. One install flag fixes it.

Original content from computingforgeeks.com - post 69022

This guide shows you how to install MySQL on Rocky Linux 10 and AlmaLinux 10 from Oracle’s Yum repository with SELinux enforcing. You pick the 9.7 or 8.4 LTS track, lock the listener down, add an application user, open remote access through firewalld and measure the result with sysbench. We ran the full path on Rocky Linux 10.2 with MySQL 9.7.2 in October 2026, and the install steps for 9.7.2 and 8.4.11 in Rocky and AlmaLinux 10.2 containers, one of them booted with systemd.

Pick the MySQL track and the package source

Install 9.7 LTS from Oracle’s repository for a new application, and 8.4 LTS when you’re matching an existing fleet or an app that still needs mysql_native_password. Both are long-term support series that only receive bug and security fixes, and Oracle’s release model gives each LTS the same support window, so 9.7 simply starts its clock later. The authentication plugin is the difference you will feel first: 8.4 still ships the native password plugin as an opt-in, while the 9.x line removed it.

Rocky Linux 10 and AlmaLinux 10 also carry MySQL in AppStream, so the old claim that these distros only offer MariaDB is no longer true. This is what we installed and inspected from each source:

SourcePackageVersion testedMain config fileError log
Rocky AppStreammysql8.4-server8.4.11/etc/my.cnf.d/mysql-server.cnf/var/log/mysql/mysqld.log
Oracle repo, 9.7 LTS (default)mysql-community-server9.7.2/etc/my.cnf/var/log/mysqld.log
Oracle repo, 8.4 LTSmysql-community-server8.4.11/etc/my.cnf/var/log/mysqld.log

The AppStream package is fine if 8.4 is all you need and you want updates from the distro mirrors. We’d still use Oracle’s repository on a database server, because it is the only source of 9.7, it gets point releases on Oracle’s schedule rather than the rebuild’s, and you can switch tracks without changing package names. Note the config path in the table: Oracle’s /etc/my.cnf has no !includedir /etc/my.cnf.d line, so the /etc/my.cnf.d/mysql-server.cnf edits in older guides do nothing on this install. The rest of this page uses Oracle’s repository.

Prerequisites

Size the server from the data, not from our lab VM. InnoDB wants its buffer pool to hold the working set, and Oracle’s own my.cnf comment suggests about 70% of RAM on a dedicated host, so a 10 GB hot dataset points at 16 GB of RAM or more. Disk is the dataset plus binary logs plus about 30% headroom, on storage with fast fsync, because every committed write waits on it. Most production OLTP boxes we have built land between 8 and 64 GB of RAM. You need:

  • Rocky Linux 10 or AlmaLinux 10, SELinux enforcing
  • a user with sudo rights
  • outbound HTTPS to dev.mysql.com and repo.mysql.com
  • TCP 3306 reachable from your app subnet, only if remote clients connect

Our VM had 2 vCPUs, 4 GB of RAM and a 40 GB disk, which is a floor for following along, not a production recommendation. If you are still choosing between engines or want a deeper grounding in InnoDB internals before you size anything, our list of books for learning MySQL and MariaDB is a good place to start.

Set the shell variables

The database name, application user, password and client network appear in several commands, so set them once. APP_SUBNET is the network your application servers live on in CIDR form for firewalld, and APP_HOSTS is the same network written as a MySQL host pattern. You only need those two if you open remote access later:

export DB_NAME="appdb"
export DB_USER="appuser"
export DB_PASS="App#Pass2026x"
export APP_SUBNET="10.0.1.0/24"
export APP_HOSTS="10.0.1.%"

Pick your own password: the default policy wants at least 8 characters with upper case, lower case, a digit and a symbol. The variables only live in the current shell, so export them again if you reconnect.

Add the MySQL Yum repository

Oracle publishes one repository setup package per release series. The current one defines both LTS tracks and enables 9.7 by default, so it works for either choice. Check the MySQL Yum repository download page for a newer file name before you run this, since Oracle bumps the trailing number when it rotates signing keys.

sudo dnf install -y https://dev.mysql.com/get/mysql97-community-release-el10-1.noarch.rpm #https://dev.mysql.com/downloads/repo/yum/

List what it enabled. You want the server and tools repositories for one track plus the connectors repository, and nothing from the innovation track, which gets a new minor release every quarter.

dnf repolist enabled | grep -i mysql

A fresh install shows the 9.7 LTS pair.

mysql-9.7-lts-community                 MySQL 9.7 LTS Community Server
mysql-connectors-community              MySQL Connectors Community
mysql-tools-9.7-lts-community           MySQL Tools 9.7 LTS Community

For 8.4 LTS, swap the two repository pairs before installing anything. Doing it afterwards means removing the 9.7 packages and then moving /var/lib/mysql and /var/log/mysqld.log out of the way, because a data directory that 9.7 has already initialized can’t be started by 8.4. If you know you’ll only ever run 8.4, Oracle also publishes a mysql84-community-release-el10 package that enables 8.4 by default. On the combined package, the switch is:

sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --disable mysql-9.7-lts-community mysql-tools-9.7-lts-community
sudo dnf config-manager --enable mysql-8.4-lts-community mysql-tools-8.4-lts-community

Run the repolist check again and the 8.4 names replace the 9.7 ones. Everything below is identical on both tracks apart from the version strings in the output.

Install MySQL without the MariaDB weak dependencies

Install the server with weak dependencies turned off. Without the flag, DNF resolves the package’s user(mysql) recommendation with five MariaDB 11.8 packages from AppStream, the transaction grows from 64 to 82 packages, and the transaction test then aborts. This is an excerpt of what we got on the first attempt:

Installing weak dependencies:
 mariadb11.8-backup               x86_64  3:11.8.8-1.el10_2         appstream                6.8 M
 mariadb11.8-client-utils         noarch  3:11.8.8-1.el10_2         appstream                 66 k
 mariadb11.8-gssapi-server        x86_64  3:11.8.8-1.el10_2         appstream                 16 k
 mariadb11.8-server               x86_64  3:11.8.8-1.el10_2         appstream                 10 M
 mariadb11.8-server-utils         noarch  3:11.8.8-1.el10_2         appstream                 25 k
...
Error: Transaction test error:
  file /usr/bin/mysql conflicts between attempted installs of mariadb11.8-3:11.8.8-1.el10_2.x86_64 and mysql-community-client-9.7.2-1.el10.x86_64
  file /usr/bin/mysqladmin conflicts between attempted installs of mariadb11.8-3:11.8.8-1.el10_2.x86_64 and mysql-community-client-9.7.2-1.el10.x86_64

Nothing is installed when that happens, so you can rerun straight away. The working command:

sudo dnf install -y --setopt=install_weak_deps=False mysql-community-server

The package scriptlet still creates the mysql account, so you lose nothing by skipping the recommendation. Confirm the version and that no MariaDB package came along:

mysqld --version
rpm -qa | grep -iE 'maria|mysql' | sort

Only Oracle’s packages should be listed, all from the same track. A mix of 9.7 and 8.4 versions means the repository switch happened after a partial install.

/usr/sbin/mysqld  Ver 9.7.2 for Linux on x86_64 (MySQL Community Server - GPL)
mysql97-community-release-el10-1.noarch
mysql-community-client-9.7.2-1.el10.x86_64
mysql-community-client-plugins-9.7.2-1.el10.x86_64
mysql-community-common-9.7.2-1.el10.x86_64
mysql-community-icu-data-files-9.7.2-1.el10.x86_64
mysql-community-libs-9.7.2-1.el10.x86_64
mysql-community-server-9.7.2-1.el10.x86_64

If you ever see a mariadb line here, something else on the box asked for it. MariaDB’s server and MySQL’s packages ship the same files, including /usr/sbin/mysqld and the mysqld unit, so DNF refuses any transaction that would install both.

Start MySQL and confirm SELinux confines it

Enable and start the service. The first start initializes the data directory and generates the root password, which took about 10 seconds on our VM, so don’t panic if the command seems to hang.

sudo systemctl enable --now mysqld

Then check three things at once: the unit state, the SELinux domain of the process and the label on the data directory:

systemctl is-active mysqld
ps -eZ | grep [m]ysqld
ls -Zd /var/lib/mysql

A healthy install runs as mysqld_t on a mysqld_db_t directory. Anything else in the first column means the policy isn’t applied.

active
system_u:system_r:mysqld_t:s0     51359 ?        00:00:00 mysqld
system_u:object_r:mysqld_db_t:s0 /var/lib/mysql

Skipping weak dependencies also skipped the separate mysql-selinux package, and that turned out not to matter. The targeted policy on Rocky Linux 10 already carries a mysql module, so the server was confined from the first start. sudo ausearch -m avc -ts today returned no denials across every step on this page. If the process shows unconfined_service_t instead, install mysql-selinux and restart rather than switching SELinux off.

Secure the root account

The first start generates a temporary root password that has already expired, so nothing can use it except a password change. It’s written to the error log.

sudo grep 'temporary password' /var/log/mysqld.log

Then run the hardening script and paste that password at the first prompt. The new password has to pass the MEDIUM policy covered below.

sudo mysql_secure_installation

On 9.7.2 the first run only replaces the expired password. It asks for the new one twice and then drops back to the shell with exit code 1 and no message. That looks like a failure but is not one: we hit it on a fresh Rocky Linux data directory and again in a fresh AlmaLinux container, and the new password worked both times. Run the same command a second time, log in with the new password and answer the remaining questions. This is the second run:

The 'validate_password' component is installed on the server.
The subsequent steps will run with the existing configuration
of the component.
Using existing password for root.

Estimated strength of the password: 100
Change the password for root ? ((Press y|Y for Yes, any other key for No) : n

 ... skipping.
...
Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Success.
...
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Success.
...
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
 - Dropping test database...
Success.
...
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

All done! 

That excerpt drops the explanatory paragraphs between prompts. Answer yes to every removal question. Oracle’s RPM installs and enables the validate_password component at first start, before the script runs, with the MEDIUM policy, which is why every password on this page mixes cases, digits and a symbol.

Keep MySQL on localhost

Oracle’s packages listen on every interface the moment the service starts: port 3306 for the classic protocol and 33060 for the X Protocol. Nothing on a fresh server needs that, so bind both to loopback now and open exactly one port later if remote clients need it. Open the main config file:

sudo vim /etc/my.cnf

Add the last two lines to the end of the [mysqld] section. This is the whole file as we ran it:

# For advice on how to change settings please see
# https://dev.mysql.com/doc/refman/9.7/en/server-configuration-defaults.html

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove the leading "# " to disable binary logging
# Binary logging captures changes between backups and is enabled by
# default. It's default setting is log_bin=binlog
# disable_log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M

datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
bind-address=127.0.0.1
mysqlx-bind-address=127.0.0.1

Restart and check the sockets. The service takes a couple of seconds to come back.

sudo systemctl restart mysqld
sudo ss -tlnp | grep mysqld

Both listeners should now show 127.0.0.1. If you still see *:3306, the lines went into the wrong file or the wrong section:

LISTEN 0      70         127.0.0.1:33060      0.0.0.0:*    users:(("mysqld",pid=61607,fd=19))
LISTEN 0      4096       127.0.0.1:3306       0.0.0.0:*    users:(("mysqld",pid=61607,fd=23))

Port 33060 stays on loopback for the rest of this guide, and 3306 only moves if you follow the optional remote section.

Create the database and application user

Your application should never log in as root, so give it its own account with rights on one database. This version is for an app running on the same server; you will be asked for the root password you set above:

mysql -u root -p -e "CREATE DATABASE ${DB_NAME}; CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}'; GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';"

Log in as the new user to prove the grant works. You will be prompted for the application password this time, not the root one.

mysql -u "${DB_USER}" -p "${DB_NAME}" -e "SELECT CURRENT_USER(), DATABASE(), @@version;"

The user, the database and the server version come back on one row. An Access denied here usually means a typo in the exported password.

CURRENT_USER()	DATABASE()	@@version
appuser@localhost	appdb	9.7.2

No FLUSH PRIVILEGES is needed after CREATE USER and GRANT, because those statements update the grant tables in memory themselves. If your app only connects locally, the core setup is done here and the next section is optional.

Allow remote MySQL connections (optional)

Remote access needs three gates opened together, and missing any one of them gives a different error. In order:

  1. MySQL listens on the network interface
  2. an account exists for the client’s host pattern
  3. firewalld accepts 3306 from the client subnet

Change the classic listener to all addresses and leave the X Protocol on loopback. Few applications use port 33060, and it is one less service exposed.

sudo sed -i "s/^bind-address=127.0.0.1/bind-address=0.0.0.0/" /etc/my.cnf
sudo systemctl restart mysqld

Create a second account for the same user name, scoped to the app network. MySQL treats appuser@localhost and [email protected].% as two separate accounts, each with its own password and grants:

mysql -u root -p -e "CREATE USER '${DB_USER}'@'${APP_HOSTS}' IDENTIFIED BY '${DB_PASS}'; GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'${APP_HOSTS}';"

Cloud and minimal images often ship without firewalld at all, and ours did, so the block below installs and starts it first; on a full install those two commands change nothing. The default public zone already allows the ssh service on port 22, so starting it will not cut your session. If your SSH daemon listens on another port, add that port first with sudo firewall-offline-cmd --zone=public --add-port=2222/tcp (using your port) before the enable --now line, or you will lock yourself out and need the console to recover. Then it allows 3306 from the app subnet alone, using a rich rule rather than opening the port to everyone the way --add-port would:

sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=${APP_SUBNET} port port=3306 protocol=tcp accept"
sudo firewall-cmd --reload
sudo firewall-cmd --list-rich-rules

The listing echoes the rule back with quotes added. A missing line means the --permanent rule was added but the reload didn’t run.

rule family="ipv4" source address="10.0.1.0/24" port port="3306" protocol="tcp" accept

Now test from a machine inside that subnet, with the server’s address in place of 10.0.1.50. We used an Ubuntu box with the stock 8.4 client:

mysql -h 10.0.1.50 -u appuser -p appdb -e "SELECT CURRENT_USER(), @@hostname, @@version; SHOW STATUS LIKE 'Ssl_cipher';"

The account that matched is the subnet one, and the session is encrypted even though we configured no certificates, because the server generates a self-signed set on first start and clients prefer TLS by default:

CURRENT_USER()	@@hostname	@@version
[email protected].%	db01	9.7.2
Variable_name	Value
Ssl_cipher	TLS_AES_128_GCM_SHA256

Those self-signed certificates encrypt the link but do not prove the server’s identity, so a client cannot detect a man in the middle. For anything crossing an untrusted network, replace them with real ones using our guide to MySQL TLS certificates and make clients verify them. The finished server shows the listener, the SELinux domain and the firewall rule in one view.

MySQL 9.7 LTS running on Rocky Linux 10 with SELinux enforcing and a firewalld rich rule for port 3306

For a fleet of app servers, the next step is usually a proxy in front of MySQL rather than more firewall rules, and our ProxySQL read/write split walkthrough picks up from a server like this one.

Measure throughput with sysbench

A benchmark on the box you just built tells you more than any default does, and the one setting worth testing first is the buffer pool, which Oracle leaves at 128 MB. sysbench is in EPEL:

sudo dnf install -y epel-release
sudo dnf install -y sysbench

We created a throwaway database and user first. The password has to pass the same MEDIUM policy as everything else:

mysql -u root -p -e "CREATE DATABASE sbtest; CREATE USER sbtest@localhost IDENTIFIED BY 'Sb#Pass2026x'; GRANT ALL ON sbtest.* TO sbtest@localhost;"

Then every run was kept identical so only the pool size changed:

  • 8 tables of 200,000 rows, 449 MB on disk
  • oltp_read_write with 4 threads
  • a 30 second warm-up, then a 60 second measured run
  • a mysqld restart before each pool size

The dataset is more than three times the default pool, which is the point: a test that fits in 128 MB tells you nothing about the setting. These are the prepare and run commands.

sysbench oltp_read_write --mysql-host=127.0.0.1 --mysql-user=sbtest --mysql-password='Sb#Pass2026x' --mysql-db=sbtest --tables=8 --table-size=200000 --threads=4 prepare
sysbench oltp_read_write --mysql-host=127.0.0.1 --mysql-user=sbtest --mysql-password='Sb#Pass2026x' --mysql-db=sbtest --tables=8 --table-size=200000 --threads=4 --time=60 run

Then we added innodb_buffer_pool_size=2G at the end of the [mysqld] section in /etc/my.cnf, restarted, and ran the same test again:

Buffer poolTransactions/sQueries/sAvg latency95th percentile
128 MB (default)58.941178.8167.60 ms207.82 ms
2 GB84.421688.3447.38 ms134.90 ms

The bigger pool gave 43% more transactions and cut the 95th percentile by a third, because reads that used to go to disk now come from memory. Do not read the absolute numbers as a MySQL ceiling, though. Even the fastest transaction took about 9 ms, which is the fsync cost of this VM’s shared storage, and that is what really limits the write rate on this VM. Size your own pool from free -m on a dedicated box (about 70% of RAM), and rerun the test after every change so you measure your storage rather than ours.

Troubleshoot MySQL on Rocky Linux 10

Each error below came up in our lab, and the fix is the one we applied. The weak dependency conflict is covered in the install section, so it isn’t repeated here.

ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

The password policy component rejects anything without upper case, lower case, a digit and a symbol, and it applies to CREATE USER as well as to root. We hit it with password123. Use a stronger password rather than lowering validate_password.policy, because the policy is part of what mysql_secure_installation just set up.

ERROR 2003 (HY000): Can’t connect to MySQL server on ‘10.0.1.50:3306’ (111)

The number in brackets is the operating system’s error, and it usually tells you which gate is shut. From a client on the server’s own subnet we got “No route to host” (errno 113) with the firewalld rule removed, because the public zone answers unknown traffic with an ICMP reject, and “Connection refused” (errno 111) with the rule in place but bind-address back on 127.0.0.1, because nothing listened on the LAN address. So 113 points at the firewall and 111 at the listener. Our remote mysql client reached the server through a NAT router, though, and it printed the 111 above while only the firewall rule was missing, so check both firewall-cmd --list-rich-rules and ss -tlnp before changing anything. An Access denied error instead means you got through the firewall and the account’s host pattern does not match the client address.

Settings in /etc/my.cnf.d/ have no effect

The Oracle server package owns an empty /etc/my.cnf.d directory, but its /etc/my.cnf has no !includedir line. We dropped a bind-address file there and my_print_defaults mysqld printed only the datadir, socket, log and pid settings from /etc/my.cnf. Put your changes in /etc/my.cnf, or add !includedir /etc/my.cnf.d to it if you prefer drop-in files, then confirm with my_print_defaults mysqld before you restart. When SELinux is the suspect rather than the config, our SELinux troubleshooting guide walks through reading the audit log.

Running Ubuntu or Debian instead? The same two tracks install from Oracle’s APT repository in our MySQL on Ubuntu and Debian guide, and if you’d rather stay with the drop-in replacement, MariaDB on Rocky Linux needs no extra repository at all.

Keep reading

Patch PHP2Shell on RHEL, Rocky Linux and AlmaLinux 10 / 9 / 8 AlmaLinux Patch PHP2Shell on RHEL, Rocky Linux and AlmaLinux 10 / 9 / 8 Customize KDE Plasma Desktop with Themes|Modules|Extensions AlmaLinux Customize KDE Plasma Desktop with Themes|Modules|Extensions Linux Terminal Shortcuts Cheat Sheet (Bash and zsh) Cheat Sheets Linux Terminal Shortcuts Cheat Sheet (Bash and zsh) Best PL-300 Books for the Power BI Data Analyst Exam Books Best PL-300 Books for the Power BI Data Analyst Exam Best MySQL and MariaDB Books for 2026 Books Best MySQL and MariaDB Books for 2026 Install PHP 8.2 on Amazon Linux 2 (and Move Off PHP 8.1) Databases Install PHP 8.2 on Amazon Linux 2 (and Move Off PHP 8.1)

Leave a Comment

Press ESC to close