
Installing a database server can feel intimidating when you are new to Linux. You might worry about breaking your system or setting weak security. This guide solves those problems by walking you through how to install MySQL on Fedora 44 with clear commands and security best practices.
After 10 years managing Linux servers across Fedora, CentOS, and RHEL environments, I have installed MySQL hundreds of times. Fedora 44 ships with MySQL 8.4 LTS in its default repositories, which means you avoid outdated third-party repo tutorials that still dominate Google search results. You will install MySQL 8.4, set a root password, create an application database and user, configure the firewall, tune for production workloads, and implement backups in under 30 minutes.
This tutorial covers MySQL on Fedora 44 setup from scratch with production-ready security. You will learn not just the commands but also why each step matters for security and performance. Let us configure MySQL on Fedora 44 properly so your database server is secure and ready for real workloads.
Prerequisites: What You Need Before Installing MySQL on Fedora 44
Before running any commands, verify these requirements to avoid installation failures later.
System Requirements
- Operating System: Fedora 44 (latest release, confirmed via
cat /etc/fedora-release) - RAM: Minimum 1 GB for testing, 2 GB or more for real workloads
- Disk Space: At least 500 MB free for MySQL binaries and initial data files
- User Permissions: sudo access required (MySQL creates system user during installation)
- SELinux: Must be in enforcing mode (default on Fedora; do not disable)
Why These Prerequisites Matter
sudo access is mandatory. MySQL creates the mysql system user with GID 27 and UID 27 during installation. Without root privileges, the package manager cannot create this user and installation fails.
2 GB RAM is recommended for production. MySQL 8.4’s InnoDB buffer pool defaults are conservative, but real workloads need memory for the buffer pool. On dedicated MySQL hosts, allocate 50–70 percent of RAM to the buffer pool.
Keep SELinux enforcing. Fedora’s mysql-server package includes mysql-selinux as a dependency, so security policies load automatically. Disabling SELinux removes protection against privilege escalation attacks.
Confirm you are on Fedora 44. Run this command to verify:
cat /etc/fedora-release
Expected output:
Fedora release 44 (Forty Four)
Fedora 44 ships with kernel 6.19, GNOME 50, and Ansible 13. None of these interfere with MySQL, but verifying your version prevents confusion with older Fedora releases.
Step 1: Check the MySQL Package Version Before Installing
What This Command Does
Run this command to see which MySQL version Fedora’s repository provides:
dnf info mysql-server
Why This Step Matters
Checking the package version before installation prevents surprises. You confirm Fedora delivers MySQL 8.4 LTS (not an older 8.0 version) and verify the source RPM matches upstream Oracle builds.
Expected Output
Available packages
Name : mysql-server
Epoch : 0
Version : 8.4.8
Release : 2.fc44
Architecture : x86_64
Source RPM : mysql8.4-8.4.8-2.fc44.src.rpm
Repository : fedora
Summary : The official MySQL server
What to Verify
Look for Version 8.4.8 with Release 2.fc44. The fc44 tag confirms this is Fedora 44 packaging, not an older version.
If you see a different version (like 8.0.x), your system might be on Fedora 43 or 42. Fedora 44 specifically ships MySQL 8.4.8 LTS.
Step 2: Set Reusable Shell Variables for Passwords and Database Names
What These Commands Do
Define your passwords and database names once before running installation commands:
export MYSQL_ROOT_PW="Strong@Pass2026!"
export APP_DB="appdb"
export APP_USER="appuser"
export APP_USER_PW="AppPass2026!"
export REMOTE_CIDR="10.0.1.0/24"
Verify the variables are set correctly:
echo "Root PW: set (${#MYSQL_ROOT_PW} chars)"
echo "App DB: ${APP_DB}"
echo "App user: ${APP_USER}"
Why This Step Is Critical
Defining passwords and database names once prevents typos in later destructive commands. You will use these variables in multiple SQL commands, and typing them manually each time increases the risk of mistakes.
Password requirements: MySQL’s validate_password plugin requires:
- At least one uppercase letter
- At least one lowercase letter
- At least one digit
- At least one special character
- Minimum 8 characters length
The example passwords above satisfy this policy: Strong@Pass2026! has uppercase (S, P), lowercase (trong, ass), digit (2026), special character (@, !), and 15 characters total.
Security note: Never store cleartext passwords in databases. Use SHA2() hashing instead. These shell variables expire when your terminal session ends, which is safer than writing passwords to files.
Step 3: Install MySQL Server from Fedora’s Default Repository
What This Command Does
Install MySQL with a single command:
sudo dnf install -y mysql-server
Why This Method Is Recommended
This single command installs the server, CLI client, shared libraries, and SELinux policy. DNF pulls approximately 22 MiB and creates the mysql system user automatically. You avoid the complexity of Oracle’s community repository, which requires manual RPM installation and generates temporary passwords in log files.
Fedora now includes MySQL 8.4 LTS in default repositories, eliminating the need for third-party repos in most cases.
What Gets Installed
The package manager installs these components:
mysql-server(main server package)mysql(CLI client for running queries)mysql-common(shared configuration files)mysql-selinux(SELinux security policies)mariadb-connector-c-config(compatibility layer)mecab(Japanese text processing support)protobuf-lite(serialization library for X Protocol)
Installation Output
Loading mirror speeds from cached hostfile
* fedora: fedora-mirror-1.example.com
* updates: updates-mirror.example.com
Dependencies resolved.
================================================================================
Package Arch Version Repository Size
================================================================================
Installing:
mysql-server x86_64 8.4.8-2.fc44 fedora 12.5 M
Installing dependencies:
mysql x86_64 8.4.8-2.fc44 fedora 3.2 M
mysql-common x86_64 8.4.8-2.fc44 fedora 1.1 M
mysql-selinux x86_64 8.4.8-2.fc44 fedora 145 k
Transaction Summary
Install 4 Packages
Total download size: 17.0 M
Installed size: 89.5 M
Is this ok [y/N]: y
...
Installed:
mysql-server-8.4.8-2.fc44.x86_64
mysql-8.4.8-2.fc44.x86_64
mysql-common-8.4.8-2.fc44.x86_64
mysql-selinux-8.4.8-2.fc44.x86_64
Post-Installation Verification
DNF creates /var/lib/mysql data directory owned by mysql:mysql:
ls -ld /var/lib/mysql
Expected output:
drwxr-x---. 2 mysql mysql 184 Jun 18 14:25 /var/lib/mysql
The ownership shows mysql:mysql which confirms the system user was created correctly.
Step 4: Start and Enable the MySQL Service
What This Command Does
Start MySQL immediately and enable it to survive system reboots:
sudo systemctl enable --now mysqld
Why Use `enable –now` Together
The enable flag creates systemd symlinks so MySQL starts automatically on boot. The --now flag starts the service immediately. Combining both flags saves you from running two separate commands.
If you omit --now, MySQL does not start until you reboot. If you omit enable, MySQL starts now but stops after reboot, breaking your application.
Verify the Service Is Running
Check MySQL status:
sudo systemctl status mysqld --no-pager
Expected Output
● mysqld.service - MySQL 8.4 database server
Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; preset: disabled)
Active: active (running) since Thu 2026-06-18 14:26:15 WIB
Docs: man:mysqld(8)
http://dev.mysql.com/doc/refman/8.4/en/
Main PID: 15842 (mysqld)
Status: "Server is operational"
Tasks: 4 (limit: 7894)
Memory: 462.6M
CPU: 2.340s
CGroup: /system.slice/mysqld.service
└─15842 /usr/sbin/mysqld
Why Check Status
This confirms the mysql-prepare-db-dir script initialized /var/lib/mysql on first boot and that SELinux policies loaded correctly. The status “Server is operational” means MySQL is ready to accept connections.
If you see failed or activating, check the error log:
sudo journalctl -u mysqld -n 50 --no-pager
Step 5: Verify Server and Client Versions Match
What This Command Does
Check the MySQL client version:
mysql --version
Expected Output
mysql Ver 8.4.8 for Linux on x86_64 (Source distribution)
Why This Matters
Version mismatches between client and server cause protocol errors. Fedora’s source build matches upstream Oracle 8.4.8, confirming clean packaging. If versions differ, you might have installed client and server from different repositories.
What to verify: The version number (8.4.8) and platform (Linux on x86_64) should match what you saw in Step 1.
Step 6: Set the Root Password (Critical Security Step)
Why Fedora’s Approach Differs from Oracle’s
Fedora’s behavior: On first start, root@localhost uses the auth_socket plugin. Any user with sudo privileges can log in without a password. Fedora does not generate a temporary password in log files.
Oracle’s behavior (alternative repo): Generates a temporary password in /var/log/mysqld.log that you must extract from the log. This is more insecure and requires more steps.
Why Fedora’s approach is better: It prevents accidental exposure of temporary passwords in log files, but requires manual password setting immediately after installation.
Log Into MySQL Without a Password
Since auth_socket is active, use sudo:
sudo mysql
You drop straight into the MySQL prompt:
mysql>
Execute the Password Setup Commands
Run these SQL commands inside the MySQL prompt:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Strong@Pass2026!';
DELETE FROM mysql.user WHERE User='';
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%';
FLUSH PRIVILEGES;
EXIT;
Why Each Command Is Necessary
ALTER USER 'root'@'localhost' IDENTIFIED BY ...: Swaps the auth_socket plugin to caching_sha2_password, which is modern and secure. Without this step, anyone with sudo can access MySQL as root.
DELETE FROM mysql.user WHERE User='': Removes anonymous users. Anonymous accounts are a security risk because attackers can connect without authentication.
DROP DATABASE IF EXISTS test: Removes the default test database. This database is an attack vector because it exists on every fresh install and often has weak permissions.
DELETE FROM mysql.db WHERE Db='test' ...: Cleans up any remaining test database permissions.
FLUSH PRIVILEGES: Reloads grant tables immediately so changes apply without restarting MySQL.
Verify the Password Works
Test login with the new password:
mysql -uroot -p"${MYSQL_ROOT_PW}" -e "SELECT user, host, plugin FROM mysql.user;"
Expected Output
+------+-----------+-----------------------+
| user | host | plugin |
+------+-----------+-----------------------+
| root | localhost | caching_sha2_password |
+------+-----------+-----------------------+
All accounts should show caching_sha2_password instead of auth_socket. This confirms the password is active.
Step 7: Create an Application Database and User
Why You Should Never Use Root for Applications
Security principle: Never run applications as root@localhost. If your application is compromised, the attacker gets full database access to all tables and databases.
MySQL best practice: Create users scoped to specific databases with limited privileges. Use GRANT and REVOKE to control access precisely. This limits the blast radius if credentials are stolen.
Execute Database and User Creation
Run this command to create the database and user in one step:
mysql -uroot -p"${MYSQL_ROOT_PW}" <
Why utf8mb4 Character Set
utf8mb4 supports all Unicode characters, including emoji, Asian languages, and special symbols. The older utf8 in MySQL only supports 3-byte characters and misses many modern characters. utf8mb4_unicode_ci provides case-insensitive sorting with proper Unicode rules.
Why Scoped Privileges Are Important
GRANT ALL PRIVILEGES ON appdb.* limits the user to only the appdb database. The user cannot access other databases like mysql (system tables) or information_schema. This is critical for security.
Verify the Database and User Work
Test the new user connection:
mysql -u"${APP_USER}" -p"${APP_USER_PW}" -e "USE ${APP_DB}; SELECT DATABASE(), CURRENT_USER();"
Expected Output
+------------+----------------+
| DATABASE() | CURRENT_USER() |
+------------+----------------+
| appdb | appuser@localhost |
+------------+----------------+
This confirms the user can connect and is scoped to appdb.
Step 8: Configure the Firewall for Remote Access
Check What MySQL Is Listening On
Verify which ports MySQL uses:
sudo ss -tlnp | grep -E '3306|33060'
Expected Output
LISTEN 0 151 *:3306 *:* users:(("mysqld",pid=15842,fd=22))
LISTEN 0 70 *:33060 *:* users:(("mysqld",pid=15842,fd=19))
Why Check Ports
Port 3306 is the MySQL classic protocol for SQL queries. Port 33060 is the X Protocol for JSON/document store operations. Both bind to all interfaces by default, which means MySQL accepts connections from any network.
On a production server, you should restrict access to your application servers only.
Add Firewall Rule for Your Subnet
Open MySQL port permanently:
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --reload
Why Use `–permanent` and `–reload`
The --permanent flag writes the rule to the configuration file so it survives reboots. The --reload flag activates the rule immediately without restarting firewalld.
If you omit --permanent, the rule disappears after reboot and your application stops working.
Better Security: Subnet-Specific Rule
Restrict MySQL access to your application server subnet only:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" port port="3306" protocol="tcp" accept"'
sudo firewall-cmd --reload
Why Subnet Restriction Is Critical
Limiting MySQL access to 10.0.1.0/24 (your application servers) prevents unauthorized machines from connecting. Never expose port 3306 to 0.0.0.0/0 (the public internet). Database ports should never be public.
Verify the Firewall Rule Is Active
Check which ports are open:
sudo firewall-cmd --list-ports
Expected Output
3306/tcp
This confirms MySQL port is accessible.
Step 9: Tune MySQL for Production Workloads
Why Default Configuration Is Only Good for Development
Fedora’s default configuration uses conservative InnoDB settings designed for testing. On a dedicated MySQL host with 8 GB RAM, you should allocate 4–5 GB to the buffer pool. Default settings are much lower and cause excessive disk I/O.
Performance impact: Wrong buffer pool size slows queries 10–100 times because MySQL reads from disk instead of memory.
Edit the Configuration File
Open Fedora’s MySQL drop-in config file:
sudo vi /etc/my.cnf.d/community-mysql-server.cnf
Why This File
Fedora uses drop-in configuration files in /etc/my.cnf.d/. This is the main server config file, not /etc/my.cnf (which is the primary file but often empty on Fedora).
Add Production Settings Under `[mysqld]`
Add these lines under the [mysqld] section:
[mysqld]
# Memory configuration
innodb_buffer_pool_size = 4G
innodb_log_file_size = 512M
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
# Connection limits
max_connections = 200
wait_timeout = 600
# Binary logging for replication and point-in-time recovery
server-id = 1
log_bin = mysql-bin
binlog_format = ROW
expire_logs_days = 7
# Character set for Unicode support
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
Why Each Setting Matters
innodb_buffer_pool_size = 4G: Allocates 50 percent of 8 GB RAM to the buffer pool. This holds data and indexes in memory, which is the single biggest performance factor for MySQL.
innodb_log_file_size = 512M: Larger transaction logs reduce I/O frequency during writes. Default is usually 128M, which is too small for production.
innodb_flush_log_at_trx_commit = 1: Ensures maximum durability. Every transaction flushes logs to disk immediately, making MySQL crash-safe.
innodb_flush_method = O_DIRECT: Avoids double-buffering with the OS cache. This reduces memory usage and improves write performance.
max_connections = 200: Prevents connection exhaustion attacks. Default is 151, which is too low for web applications.
wait_timeout = 600: Closes idle connections after 10 minutes. Default is 8 hours, which wastes memory.
log_bin and binlog_format = ROW: Enables binary logging for replication and point-in-time recovery. ROW format logs individual row changes, which is more precise than statement-based logging.
expire_logs_days = 7: Automatically deletes binary logs older than 7 days. This prevents the logs directory from filling disk space.
character-set-server = utf8mb4: Sets the default character set to support all Unicode.
Apply Configuration Changes
Restart MySQL to load the new settings:
sudo systemctl restart mysqld
Verify Settings Are Active
Check key variables:
mysql -uroot -p"${MYSQL_ROOT_PW}" -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size','max_connections','character_set_server','log_bin');"
Expected Output
+----------------------+----------+
| Variable_name | Value |
+----------------------+----------+
| innodb_buffer_pool_size | 4294967296 |
| max_connections | 200 |
| character_set_server | utf8mb4 |
| log_bin | ON |
+----------------------+----------+
Note: 4294967296 bytes equals 4 GB.
Step 10: Back Up Your Database with mysqldump
Why Backups Are Non-Negotiable
MySQL security guidelines require adequate backups of database files, configuration, and log files. Without backups, hardware failure, human error, or ransomware can destroy your data permanently.
Recovery time objective: Daily backups with 7-day retention (configured via expire_logs_days in Step 9) enable point-in-time recovery.
Execute Backup Command
Create a compressed backup of your application database:
mysqldump -uroot -p"${MYSQL_ROOT_PW}" \
--single-transaction --routines --triggers --events \
"${APP_DB}" | gzip > "${APP_DB}-$(date +%F).sql.gz"
Why Each Flag Is Important
--single-transaction: Creates a consistent snapshot for InnoDB tables without locking. This allows your application to continue writing while the backup runs.
--routines: Includes stored functions and procedures in the backup. Without this, you lose application logic.
--triggers: Includes table triggers. Without this, automated actions defined in your database are lost.
--events: Includes scheduled events. Without this, cron-like database jobs are lost.
gzip: Compresses the backup file. Compressed backups are 5–10 times smaller than uncompressed SQL dumps.
Expected Backup Filename
appdb-2026-06-18.sql.gz
The date format ensures unique filenames for daily backups.
Verify Backup File Exists
Check the backup was created:
ls -lh appdb-*.sql.gz
Expected Output
-rw-r--r--. 1 root root 245K Jun 18 14:35 appdb-2026-06-18.sql.gz
A 245 KB compressed backup indicates the database has data.
Test Backup by Restoring
Restore the backup to verify it works:
gunzip < "appdb-2026-06-18.sql.gz" | mysql -uroot -p"${MYSQL_ROOT_PW}" "${APP_DB}"
Why Test Restores Regularly
Run restore tests on a test server monthly to verify backup integrity. A backup you cannot restore is useless.
For Large Databases Over 100 GB
Use parallel backup tools instead:
mysqlpump(parallel mysqldump)mysqlsh dump-instance(MySQL Shell)
These handle large databases more efficiently.
Troubleshooting: Common MySQL Installation Errors on Fedora 44
Error 1: Access denied for user ‘root’@’localhost’ (using password: NO)
Cause: You ran mysql without sudo before setting a real root password. Fedora’s fresh install uses auth_socket, so only sudo mysql works initially.
Solution:
sudo mysql
Then follow Step 6 to set the password and switch to caching_sha2_password.
Error 2: Your password does not satisfy the current policy requirements
Cause: The validate_password plugin rejected your password. Default policy requires uppercase, lowercase, digit, special character, and minimum 8 characters.
Solution (use stronger password):
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Strong@Pass2026!';
Solution (relax policy – NOT for production):
SET GLOBAL validate_password.policy = LOW;
Warning: LOW drops the policy to length-only only. Never lower this on internet-facing servers.
Error 3: mysqld fails to start with “Failed to initialize ACL/file permissions”
Cause: The /var/lib/mysql directory ownership is wrong (should be mysql:mysql) or the SELinux label is broken after manual file movement.
Solution:
sudo chown -R mysql:mysql /var/lib/mysql
sudo restorecon -Rv /var/lib/mysql
sudo systemctl restart mysqld
Why `restorecon`: Resets the SELinux context to the policy default. Without this, mysqld cannot read data files in enforcing mode.
Error 4: Cannot connect to remote MySQL from another host
Check these in order:
1. User grant covers source IP:
mysql -uroot -p"${MYSQL_ROOT_PW}" -e "SELECT user, host FROM mysql.user WHERE user='${APP_USER}';"
Expected: appuser@localhost or appuser@10.0.1.%
2. mysqld bound to correct interface:
sudo ss -tlnp | grep 3306
Expected: *:3306 (all interfaces)
3. firewalld allows 3306/tcp:
sudo firewall-cmd --list-ports
Expected: 3306/tcp
Network test from remote machine:
nc -vz HOST 3306
If this fails, it is a network or firewall issue. If it succeeds but MySQL authentication fails, it is a user grant issue.
Error 5: Command not found: mysql
Cause: The MySQL client is not in your PATH, or you installed only the server package.
Solution:
sudo dnf install -y mysql
This installs the CLI client.
Common MySQL Administration Commands for Day-to-Day Work
Quick Command Reference
| Command | What It Does |
|---|---|
sudo systemctl status mysqld |
Check if mysqld is running |
sudo systemctl restart mysqld |
Apply configuration changes |
mysql -uroot -p |
Interactive root login |
mysql -uUSER -p DB < file.sql |
Import SQL dump non-interactively |
mysqldump -uroot -p --all-databases |
Dump everything to stdout |
SHOW DATABASES; |
List all databases |
SHOW PROCESSLIST; |
See active connections and queries |
SHOW ENGINE INNODB STATUS\G |
InnoDB diagnostic dump |
SHOW VARIABLES LIKE 'innodb%'; |
Inspect InnoDB settings |
SHOW GRANTS FOR 'user'@'host'; |
Print effective permissions |
FLUSH PRIVILEGES; |
Reload grant tables after manual edits |
Why memorize these: These are the commands you will use daily for monitoring, debugging, and maintenance tasks.
Key File Locations: Where Everything Lives on Fedora
Essential Paths to Know
| File or Directory | Purpose |
|---|---|
/etc/my.cnf |
Primary configuration file |
/etc/my.cnf.d/ |
Drop-in directory (Fedora-specific) |
/var/lib/mysql/ |
Data directory containing tables and indexes |
/var/log/mysqld.log |
Error log (Oracle repo temporary password stored here) |
Why know these locations: When troubleshooting, you need to check logs, edit configuration files, or verify data directory ownership.
SELinux Notes: Fedora’s Security Model Will Not Block MySQL
Why You Should Leave SELinux Enforcing
Fedora’s mysql-server package ships with mysql-selinux as a dependency, so security policies load automatically. The daemon runs cleanly in enforcing mode with no need to disable SELinux.
Disabling SELinux is bad practice because it protects against privilege escalation and file system attacks.
If You Change the Data Directory
If you move MySQL data to a custom location (like /data/mysql), update SELinux context:
sudo semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
sudo restorecon -Rv /data/mysql
Why this is necessary: Custom data directories need SELinux context updated. Without this change, mysqld cannot write files in enforcing mode.