If you’ve ever needed to install MySQL on Linux and ended up with seven browser tabs open, none of them telling you the same thing, you’re in good company. I’ve been there. The first time I set up a MySQL server, it was on a $5 VPS to host a WordPress site for a friend’s pottery business. I got the database running in about ten minutes — and then nearly hosed the whole box because I skipped the security step. Lesson learned, and that’s a story I’ll come back to.

This guide walks you through a clean MySQL install on both Ubuntu/Debian and RHEL/Rocky Linux/AlmaLinux. By the end, you’ll have MySQL 8.4 LTS running, a hardened configuration, a dedicated app user, and a firewall that doesn’t make your server an open buffet on port 3306. It’s the install I wish I’d had a tutorial for back in 2010.
Quick answer: To install MySQL on Linux, run sudo apt install mysql-server on Ubuntu/Debian or sudo dnf install mysql-server on RHEL/Rocky/Alma. Then run sudo mysql_secure_installation, create a dedicated app user (never use root), and lock down port 3306 in your firewall. Total time: about 15 minutes.
Before You Start: MySQL 8.4 LTS and What This Guide Covers
MySQL 8.4 is the current Long Term Support release, dated April 2024 and supported until April 2032. That’s the version your package manager will pull on any modern distro right now. If you’re following an older guide that talks about MySQL 5.7 or even 8.0, ignore the version specifics — 8.0 hit end-of-life in April 2026, and 8.4 is what you should be running. The official MySQL 8.4 Reference Manual is your friend if you want to dig into specific features.
Get a VPS from as low as $11/year! WOW!
Most “install MySQL on Linux” guides cover Ubuntu and call it a day. That always frustrates me. Plenty of us run Rocky Linux or AlmaLinux on production boxes, and the commands are different enough that copy-paste from an Ubuntu guide will leave you stuck. So this one covers both families.
According to the DB-Engines Ranking, MySQL has been the world’s most popular open-source database for over a decade, with around 40% market share among relational databases. Roughly 189,000 companies run it. That’s a lot of “it works on my machine” energy keeping the internet glued together.
Step 1: Install MySQL
The actual install is one command. The pre-flight check is one more. If you’re new to package managers in general, I’ve got a primer on installing software on Linux that explains apt vs dnf at a higher level.
On Ubuntu and Debian
Update your package index first, then pull the server package:
sudo apt update
sudo apt install mysql-server
If you’re not sure what apt update is actually doing, here’s the difference between apt update vs apt upgrade — short version, update refreshes the index, upgrade installs new versions.
Once the install finishes, confirm it landed:
mysql --version
And check the service is running using systemctl:
sudo systemctl status mysql
sudo systemctl enable mysql
The enable line makes it start on boot. On Ubuntu the service is named mysql. Remember that — it’s about to matter.
On RHEL, Rocky Linux, and AlmaLinux
On EL9-family systems, the package is in the AppStream repo as mysql-server. You don’t need Oracle’s custom MySQL repo unless you want a specific minor version they haven’t pushed to AppStream yet.
sudo dnf install mysql-server
sudo systemctl start mysqld
sudo systemctl enable mysqld
Notice the service name: mysqld, not mysql. This trips up Ubuntu folks moving to Red Hat systems constantly. I’ve watched senior engineers fumble it in a screen share. It’s fine. We’ve all been there.
Step 2: Secure Your Installation (Don’t Skip This)
Here’s where my pottery-website story turns into a cautionary tale. I did the apt install, ran mysql -u root, created a database, and called it done. I never ran the security script. About 90 minutes after pointing DNS at the box, the auth log was a wall of failed root SSH attempts and someone was already knocking on port 3306. The MySQL root account had no password. The anonymous user existed. The test database existed. I was ten minutes from a very bad day.
Run this. Always:
sudo mysql_secure_installation
The mysql_secure_installation script asks you about six things. Read each prompt. Don’t smash Y like you’re skipping a EULA.
- VALIDATE PASSWORD component: Optional. It enforces complexity rules on future passwords. I usually say yes on production boxes.
- Set root password: On Ubuntu 22.04+, you may not even need this — root uses
auth_socketby default (more on that in a second). On RHEL, set a real password. - Remove anonymous users: Yes. Always yes. The anonymous user can log in with no credentials. Why does this exist? Historical reasons. Get rid of it.
- Disallow root login remotely: Yes. Root should only ever log in from the local socket. If you need remote admin access, create a named admin user.
- Remove test database: Yes. The default
testdatabase lets any user — including the anonymous one we just hopefully removed — create tables. It’s a legacy attack vector. - Reload privilege tables: Yes. Applies your changes immediately.
The whole thing takes about 30 seconds and closes the most common attack vectors. Skipping it on an internet-facing server is, in my opinion, inexcusable.
“Never give anyone (except MySQL root accounts) access to the user table in the mysql system database!”
— Oracle, MySQL Security Guidelines
Two things to know about MySQL 8.4 specifically:
- auth_socket on Ubuntu: When the package installs on Ubuntu, root authenticates via the OS user, not a password. So
sudo mysqljust works, andmysql -u root -pwill fail. This is by design. It’s also whymysql_secure_installationmay not ask you to set a root password on a fresh Ubuntu install. - mysql_native_password is disabled by default in 8.4. If you’ve got an old app that requires it, you’ll need to use
caching_sha2_password(the default) or explicitly re-enable the legacy plugin. Most modern client libraries handlecaching_sha2_passwordfine.
Step 3: Log In and Create an Application User
Don’t use root for application connections. Ever. If your app’s database credentials leak — and at some point they will, even if it’s just into a git repo by accident — you don’t want the attacker getting full admin access. The principle of least privilege applies in databases the same way it applies to Linux file permissions.
Log in. On Ubuntu (auth_socket):
sudo mysql
On RHEL or any password-based setup:
mysql -u root -p
Once you’re at the mysql> prompt, create a database and a dedicated user:
CREATE DATABASE myappdb;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'StrongPassword!';
GRANT ALL PRIVILEGES ON myappdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
Notice the grant is scoped to myappdb.*, not *.*. That user can do whatever it wants inside its own database, but it can’t see or touch anything else. If you know the app only needs to read and write specific tables, narrow it further: GRANT SELECT, INSERT, UPDATE, DELETE. WordPress and most CMSes do need ALL on their own DB, but a reporting tool might only need SELECT.
Verify:
SHOW GRANTS FOR 'appuser'@'localhost';
Step 4: Configure Firewall and bind-address
Two layers here: MySQL’s own listener, and the system firewall. Both need to agree before MySQL is reachable from anywhere.
Check your bind-address
The config file lives at /etc/mysql/mysql.conf.d/mysqld.cnf on Ubuntu, and /etc/my.cnf on RHEL-based systems. Look for the bind-address directive:
bind-address = 127.0.0.1
That’s the default and it’s what you want for any setup where the database lives on the same box as the application. It tells MySQL to only accept connections from localhost. If you change this to your server’s public IP, you’re saying “yes, listen to the whole internet.” Don’t bind to 0.0.0.0 unless you genuinely need to and have done the firewall work to restrict who can reach it.
UFW (Ubuntu/Debian)
If you do need remote access — say, your app server is on a different host inside a VPC — restrict it to that one IP:
sudo ufw allow from 192.168.1.100 to any port 3306
Never ufw allow 3306 with no source restriction. Full guide on UFW firewall if you haven’t set it up yet.
firewalld (RHEL-based systems)
On Rocky, AlmaLinux, or RHEL, you’re using firewalld. Same idea, different syntax:
sudo firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=192.168.1.100 port port=3306 protocol=tcp accept'
sudo firewall-cmd --reload
Port 3306 is one of the most aggressively scanned ports on the internet. The bots find it within minutes of a fresh VPS going live. I’ve watched it happen in real time on a brand-new IP. Treat it accordingly.
Step 5: Basic MySQL Service Management
Day-to-day commands you’ll use forever. Substitute mysqld for mysql if you’re on RHEL.
- Start:
sudo systemctl start mysql - Stop:
sudo systemctl stop mysql - Restart:
sudo systemctl restart mysql - Reload config without restarting:
sudo systemctl reload mysql - Check status:
sudo systemctl status mysql - View recent logs:
sudo journalctl -u mysql -n 50
That last one — journalctl — is your debugging best friend. When MySQL refuses to start and the systemctl status output is too short to tell you why, journalctl will have the actual error.
Three MySQL Mistakes I See Constantly
I’ve helped enough people fix broken MySQL servers to know what trips folks up. These three are the greatest hits.
1. Skipping mysql_secure_installation. The anonymous user and test database aren’t theoretical risks. They’re live attack surfaces. Run the script.
2. Using root for every app connection. If your wp-config.php ever gets exposed (it does happen), root credentials in there means full database compromise. One app, one user, scoped privileges.
3. Binding to 0.0.0.0 with a wide-open firewall. My first homelab MySQL server, exposed on a public IP for testing, was getting brute-forced within an hour. Within an hour. Bots are fast. Lock it down or keep it on localhost.
What to Set Up Next
Now that you’ve got a working database, the natural next steps are wiring it into the rest of your stack. If you’re building a LAMP server, my guide on installing Apache web server is the next stop. Prefer the LEMP route? Set up Nginx instead. For anything web-facing, you also want Let’s Encrypt with Certbot wired up so traffic to your application server is encrypted end-to-end.
Once it’s all running, the two things you cannot skip on a real production box: automated backup setup using mysqldump on a cron, and fail2ban watching the MySQL log for brute-force attempts. For a broader hardening checklist, my roundup of the best Linux security tools covers what else belongs on a server you actually care about.
Got questions about a specific install scenario, or a weird error from mysql_secure_installation? Drop a comment — I read them all, and odds are someone else hit the same thing. Happy hacking.




