2 AM and the database refuses to connect
It started like any other deploy: I had just finished installing MariaDB on Fedora Server, ran systemctl start mariadb, tried connecting from the app server — Connection refused. Checked the firewall, checked the port, checked the config — everything looked fine. It took nearly 40 minutes to find the culprit: SELinux was silently blocking the connection, with no clear logs and no specific error messages.
Fedora moves fast with package updates — I’ve been using it as my main development machine for two years and I genuinely like that about it. The trade-off is that SELinux and firewalld here are enabled and locked down much tighter than on Ubuntu or CentOS. Better security, absolutely. Also the source of more than a few sleepless nights.
This article documents the exact process I use to get MariaDB production-ready on Fedora Server — including the SELinux context and firewalld steps that most other guides gloss over.
Why Fedora is different
On Ubuntu or CentOS 7, installing MariaDB and running mysql_secure_installation is basically all you need. Fedora Server with SELinux in enforcing mode (the default) adds three layers you need to understand upfront:
- SELinux context: Every file, process, and port carries its own context. MariaDB needs the correct context to read the datadir, open a socket, and listen on a port.
- firewalld zones: The default
publiczone blocks port 3306 entirely — no exceptions until you explicitly open it. - systemd hardening: MariaDB’s unit file on Fedora includes directives like
PrivateTmpandProtectSystem— these affect socket paths and custom datadirs.
Understanding these three points upfront will save you at least an hour or two of unnecessary debugging.
Hands-on: Step-by-step installation and configuration
Step 1: Install MariaDB
The Fedora repo already includes a fairly recent version of MariaDB. Check it before installing:
dnf info mariadb-server
Install:
sudo dnf install -y mariadb-server mariadb
Enable and start immediately:
sudo systemctl enable --now mariadb
systemctl status mariadb
If the status shows active (running), you’re good. If you see failed, run journalctl -xe -u mariadb to read the logs — the error is usually in the first few lines.
Step 2: Secure the initial installation
sudo mysql_secure_installation
The script walks you through several prompts — answer as follows:
- Set root password → Y, use a password at least 16 characters long with uppercase letters and special characters
- Remove anonymous users → Y
- Disallow root login remotely → Y (mandatory for production)
- Remove test database → Y
- Reload privilege tables → Y
Verify the login after completing:
sudo mysql -u root -p
# Running with sudo uses the unix socket and skips the password prompt:
sudo mysql
Step 3: Create a database and user for your application
Your app should never connect as root — no exceptions to this rule. Create a dedicated user with the minimum permissions required:
-- Log into MariaDB first
CREATE DATABASE myapp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'myapp_user'@'localhost' IDENTIFIED BY 'StrongPassword123!';
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp_user'@'localhost';
-- If the app is on a separate server (replace with the actual app server IP):
CREATE USER 'myapp_user'@'192.168.1.50' IDENTIFIED BY 'StrongPassword123!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'myapp_user'@'192.168.1.50';
FLUSH PRIVILEGES;
Note that GRANT ALL in the first example is only appropriate when the app and DB are on the same server and the risk is low. For remote connections, restrict permissions to exactly what’s needed, as shown in the second example.
Step 4: Open the firewall properly
Port 3306 is completely blocked by default — this step is mandatory if your app and database run on separate servers.
# Check active zones
firewall-cmd --get-active-zones
# Recommended: only allow connections from the specific app server IP
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.50" port port="3306" protocol="tcp" accept'
# Or open for the entire internal subnet (less secure)
firewall-cmd --permanent --add-service=mysql
# Apply changes
firewall-cmd --reload
# Verify
firewall-cmd --list-all
I use a rich rule instead of --add-service=mysql in production because it lets me restrict access by source IP. Only the app server can reach port 3306 — other machines on the same network are still blocked.
Step 5: SELinux — the silent blocker
This section is the main reason this article exists. SELinux on Fedora runs in enforcing mode and will block things without telling you why.
Check the current status:
getenforce
# Expected output: Enforcing
Using the default datadir (/var/lib/mysql) requires no extra steps — this path already has the mysqld_db_t context, so MariaDB works normally.
A custom datadir is where things tend to break. For example, if you move the datadir to /data/mysql:
# Check the current context — it's usually unlabeled_t or default_t
ls -laZ /data/mysql
# Assign the correct SELinux context for MariaDB
sudo semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
sudo restorecon -Rv /data/mysql
# Verify — you should now see mysqld_db_t
ls -laZ /data/mysql
# system_u:object_r:mysqld_db_t:s0
If semanage is not available:
sudo dnf install -y policycoreutils-python-utils
Debugging when SELinux blocks a connection:
# View recent AVC denials
sudo ausearch -m avc -ts recent
# Easier to read with audit2why
sudo ausearch -m avc -ts recent | audit2why
# The most powerful tool: sealert — analyzes and suggests fixes automatically
sudo dnf install -y setroubleshoot-server
sudo sealert -a /var/log/audit/audit.log
That night, audit2why revealed that MariaDB was trying to bind to a custom socket path that SELinux wouldn’t allow. Five minutes to fix by restoring the default socket path in /etc/my.cnf — the preceding 40 minutes were spent fumbling around with no idea where to look.
Step 6: Production configuration
Create a separate file instead of editing /etc/my.cnf directly — easier to manage and won’t be overwritten by package updates:
sudo vim /etc/my.cnf.d/production.cnf
[mysqld]
# Default charset — utf8mb4 supports emoji and full Unicode
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# Uncomment if the app is on the same server, to restrict listening to localhost only
# bind-address = 127.0.0.1
# Performance — set innodb_buffer_pool_size to ~50-70% of available RAM
# 512M for a 1GB RAM server, 2-3G for a 4GB RAM server
innodb_buffer_pool_size = 256M
max_connections = 100
query_cache_type = 0 # Query cache has been deprecated since MariaDB 10.1
# Error log
log_error = /var/log/mariadb/mariadb.log
# Slow query log — helps identify slow queries after deployment
slow_query_log = 1
slow_query_log_file = /var/log/mariadb/slow.log
long_query_time = 2
[client]
default-character-set = utf8mb4
Restart after changing the config:
sudo systemctl restart mariadb
systemctl status mariadb
Step 7: Test remote connectivity
From the app server or another machine on the network, test the connection:
# Replace 192.168.1.100 with the actual IP of your Fedora Server
mysql -h 192.168.1.100 -u myapp_user -p myapp_db
# If no mysql client is available, test the port with netcat first
nc -zv 192.168.1.100 3306
Still getting refused even after opening the firewall? Check whether MariaDB is binding to the right interface:
sudo ss -tlnp | grep 3306
# You should see 0.0.0.0:3306, not 127.0.0.1:3306
If you see 127.0.0.1:3306, MariaDB is only listening on localhost — review the bind-address setting in your config.
Production checklist
- MariaDB running and set to autostart:
systemctl is-enabled mariadbreturnsenabled - Remote root login disabled (handled during
mysql_secure_installation) - App uses a dedicated user with minimum required permissions, not root
- firewalld opens port 3306 only for the specific app server IP, using a rich rule
- SELinux is in enforcing mode, datadir has the correct
mysqld_db_tcontext - Slow query log is enabled to catch performance issues after deployment
- Backup plan in place:
mysqldumpfor small databases,mariabackupfor large production setups
Wrapping up
Installing MariaDB on Fedora isn’t any more complicated than on other distros — it just has a few extra steps that most guides skip because they were written for Ubuntu. SELinux and firewalld are not your enemies. They’re the reason Fedora servers get compromised less often in practice.
Don’t disable SELinux just because debugging feels like a chore. I did it once on a staging server and paid for it by explaining to the team why staging passed but production failed. Not a great conversation to have.
Running into errors after following this guide: journalctl -xe -u mariadb for MariaDB issues, ausearch -m avc -ts recent for SELinux — these two commands resolve 90% of common problems. Good luck with your deploy.
