How to Install MySQL or MariaDB
Install MySQL or MariaDB on Debian, Fedora, or Arch; secure it with mysql_secure_installation; create users with least-privilege grants; and automate backups.
Before you start
- ▸A Linux server with sudo or root access
- ▸Basic familiarity with the command line and a terminal
- ▸Sufficient disk space (at least 1 GB free in /var/lib/mysql)
MySQL and MariaDB are drop-in-compatible relational database servers that power everything from WordPress blogs to production SaaS applications. MariaDB is the community fork of MySQL with some performance and licensing advantages; pick either one—the administration commands covered here are identical unless noted. This guide walks through installation on three major distro families, the mandatory post-install hardening step, user and privilege management, and a reliable backup workflow.
Install the Database Server
Debian / Ubuntu
Ubuntu 22.04 and later default to MySQL 8.x from the official repos. MariaDB is available as a separate package. Install one or the other—never both on the same system.
# MySQL
sudo apt update && sudo apt install -y mysql-server
# --- OR ---
# MariaDB
sudo apt update && sudo apt install -y mariadb-server
Fedora / RHEL / Rocky Linux
On RHEL 9 and its clones, MySQL is not in the default repos; you either enable the MySQL module stream or install MariaDB which is in the AppStream repo.
# MariaDB (AppStream — simplest on RHEL/Rocky)
sudo dnf install -y mariadb-server
# --- OR ---
# MySQL via module stream (Fedora/RHEL 8+)
sudo dnf module enable mysql:8.0 -y
sudo dnf install -y mysql-server
Arch Linux
# MariaDB (MySQL is AUR-only on Arch)
sudo pacman -S mariadb
# Arch requires this one-time init step before first start
sudo mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
Enable and Start the Service
All three families use systemd. The service name differs slightly between MySQL and MariaDB.
# MySQL
sudo systemctl enable --now mysql
# MariaDB
sudo systemctl enable --now mariadb
Confirm it is running:
sudo systemctl status mariadb # or mysql
You should see active (running) in the output.
Secure the Installation
Both MySQL and MariaDB ship an interactive hardening script. Run it immediately after the first start—before creating any application databases.
sudo mysql_secure_installation
The script walks you through several prompts. Recommended answers:
- Set root password — Yes. Use a strong, unique password and store it in a password manager.
- Remove anonymous users — Yes.
- Disallow root login remotely — Yes. Root should only connect over the Unix socket.
- Remove test database — Yes.
- Reload privilege tables now — Yes.
MySQL 8 note: On a fresh Ubuntu/Debian MySQL install the root account uses the auth_socket plugin, so the script may skip the password prompt. This is intentional and secure—root only authenticates via sudo mysql.
Connect as Root and Verify
sudo mysql
If you set a traditional root password, use -p:
sudo mysql -u root -p
Once inside the MySQL prompt, check the server version:
SELECT VERSION();
Output will vary but should resemble 8.0.37 (MySQL) or 10.11.7-MariaDB.
Create a Database and Application User
Never let applications connect as root. Create a dedicated database and a user with only the privileges it needs.
CREATE DATABASE appdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'StrongPassw0rd!';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
Use 'appuser'@'%' (with a wildcard host) only if the application server is remote, and restrict access at the firewall level. For read-only reporting accounts, replace ALL PRIVILEGES with SELECT.
Common Grant Patterns
| Use case | Grant statement |
|---|---|
| Full app access (local) | GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost'; |
| Read-only replica/report | GRANT SELECT ON appdb.* TO 'reader'@'localhost'; |
| Backup user | GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON *.* TO 'backup'@'localhost'; |
| Replication user | GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'replica_ip'; |
Verify Grants
SHOW GRANTS FOR 'appuser'@'localhost';
Firewall Configuration
If the database is local-only (the most common and safest setup), leave port 3306 closed. If remote connections are required, open the port only for a specific IP.
# ufw (Debian/Ubuntu)
sudo ufw allow from 192.168.1.50 to any port 3306
# firewalld (Fedora/RHEL/Rocky)
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="192.168.1.50"
port protocol="tcp" port="3306" accept'
sudo firewall-cmd --reload
Also update MySQL/MariaDB to accept remote connections by changing bind-address in the config:
# MySQL: /etc/mysql/mysql.conf.d/mysqld.cnf
# MariaDB: /etc/my.cnf.d/mariadb-server.cnf (RHEL) or /etc/mysql/mariadb.conf.d/50-server.cnf (Debian)
bind-address = 0.0.0.0 # or the specific server IP
Restart the service after any config change:
sudo systemctl restart mariadb # or mysql
Backups with mysqldump
mysqldump is the simplest backup tool and ships with both servers. It produces portable SQL dumps suitable for most use cases up to tens of gigabytes.
Single Database Dump
mysqldump -u root -p --single-transaction --routines --triggers appdb \
| gzip > /var/backups/appdb_$(date +%F).sql.gz
--single-transaction takes a consistent snapshot of InnoDB tables without locking them. Always include it for production databases.
All Databases Dump
mysqldump -u root -p --all-databases --single-transaction --routines --triggers \
| gzip > /var/backups/all_dbs_$(date +%F).sql.gz
Automate with a systemd Timer
Create a credentials file so the password is not in the timer unit:
sudo tee /root/.my-backup.cnf > /dev/null <<'EOF'
[client]
user=backup
password=BackupPassw0rd!
EOF
sudo chmod 600 /root/.my-backup.cnf
Create the backup script:
sudo tee /usr/local/bin/db-backup.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
DEST=/var/backups/mysql
mkdir -p "$DEST"
mysqldump --defaults-extra-file=/root/.my-backup.cnf \
--all-databases --single-transaction --routines --triggers \
| gzip > "$DEST/all_dbs_$(date +%F_%H%M).sql.gz"
find "$DEST" -name '*.sql.gz' -mtime +14 -delete
EOF
sudo chmod +x /usr/local/bin/db-backup.sh
Create the systemd service and timer:
sudo tee /etc/systemd/system/db-backup.service > /dev/null <<'EOF'
[Unit]
Description=MySQL/MariaDB nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/db-backup.sh
EOF
sudo tee /etc/systemd/system/db-backup.timer > /dev/null <<'EOF'
[Unit]
Description=Run db-backup daily at 02:00
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now db-backup.timer
Verify the timer is scheduled:
systemctl list-timers db-backup.timer
Troubleshooting
- Service fails to start: Check
sudo journalctl -xeu mariadb. A common cause is a corrupted InnoDB log from an unclean shutdown; MariaDB usually auto-recovers, but you may need to removeib_logfile*from/var/lib/mysqlafter stopping the service. - Access denied for root: On MySQL 8 with
auth_socket, always connect withsudo mysql, notmysql -u root -p. If you need password auth, alter the root account:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password'; - Can't connect remotely: Confirm three things:
bind-addressis set correctly, port 3306 is open in the firewall, and the user account uses a non-localhost host ('user'@'%'or specific IP). - mysqldump fails mid-run: Check disk space on both the source (
/var/lib/mysql) and destination. Also verify the backup user has all required grants withSHOW GRANTS FOR 'backup'@'localhost';
Frequently asked questions
- Should I use MySQL or MariaDB?
- For new projects either works. MariaDB is the default on RHEL/Rocky and Arch, has no Oracle licensing concerns, and adds some extra storage engines. MySQL 8 has stronger JSON support and is the de facto choice for cloud-managed databases. If you're running WordPress or a typical LAMP stack, both are fine.
- How do I restore from a mysqldump backup?
- Decompress and pipe the SQL file into mysql: gunzip < backup.sql.gz | mysql -u root -p appdb. Create the target database first if it doesn't exist.
- Is mysqldump safe to use on a live production database?
- Yes, as long as you use --single-transaction for InnoDB tables. This takes a consistent snapshot without blocking writes. Avoid --lock-all-tables on live systems as it blocks writes for the entire dump duration.
- How do I change a user's password?
- Connect as root and run: ALTER USER 'appuser'@'localhost' IDENTIFIED BY 'NewPassw0rd!'; then FLUSH PRIVILEGES;. This works on both MySQL 8 and MariaDB 10.4+.
- How do I revoke a user's privileges or delete a user?
- To revoke specific privileges: REVOKE INSERT, UPDATE ON appdb.* FROM 'appuser'@'localhost';. To remove the account entirely: DROP USER 'appuser'@'localhost';. Run FLUSH PRIVILEGES; after either operation.
Related guides
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
How to Change the Webmin Port
Move Webmin off its default port 10000 by editing miniserv.conf, updating your firewall (ufw, firewalld, or nftables), and restarting the service.
Configure a UPS on Linux with NUT
Install and configure Network UPS Tools (NUT) on Linux to detect your UPS, load the right driver, and trigger a safe automatic shutdown when battery runs low.
How to Configure the SMTP HELO/EHLO Name
Set the correct SMTP HELO/EHLO hostname in Postfix and Sendmail, configure FCrDNS records, and verify your mail server won't be rejected or spam-flagged.