Securing MySQL
When you first install MySQL (a popular open-source relational database) on a server, it ships with a few insecure defaults that are fine for a quick test but dangerous in the real world: a database account with no password, an anonymous login anyone can use, and a throwaway test database that any user can write to. MySQL bundles a small helper program called mysql_secure_installation that walks you through closing all of these holes in a couple of minutes. Running it is the essential first step after every fresh install, before you create a single real table.
What mysql_secure_installation actually does
mysql_secure_installation is an interactive script (a program that asks you yes/no questions and acts on your answers). It does not do anything you couldn’t do by hand with SQL commands. It just bundles the most important hardening steps into one guided flow so you don’t forget any of them. Specifically it lets you:
- Set or change the password for the
rootaccount (the all-powerful admin user). - Turn on the validate_password component (rules that reject weak passwords).
- Remove anonymous users (accounts with a blank username that anyone can log in as).
- Disallow
rootfrom logging in remotely (over the network). - Drop the
testdatabase that ships with MySQL. - Reload the privilege tables so all of the above takes effect immediately.
When to run it: every single time, right after apt install mysql-server, on every environment from your laptop to production. There is no good reason to skip it.
Understanding root auth on Ubuntu: auth_socket vs password
Before you run the script, you need to understand one Ubuntu-specific quirk. On Ubuntu 22.04 and 24.04 LTS, the MySQL package configures the root account to use the auth_socket plugin instead of a password.
- auth_socket means MySQL trusts the operating system. If you are already logged into the Linux server as the
rootsystem user (or usingsudo), MySQL lets you in with no password. If you are any other Linux user, you are refused — even with the correct password. This is actually quite secure for a single-admin box because there is no password to steal or brute-force. - password (caching_sha2_password) is the classic model: you supply a username and password, and MySQL checks it. You need this if an application or a person must connect without being the
rootLinux user.
| auth_socket | Password (caching_sha2_password) | |
|---|---|---|
| How you log in | sudo mysql (no password) | mysql -u root -p then type password |
| Best for | The local admin on a single server | App users, remote logins, GUI tools |
| Password to steal? | None | Yes — must be stored/managed |
| Default for root on Ubuntu | Yes | No |
If
mysql_secure_installationasks you to set a root password butrootis still on auth_socket, the password you set may be ignored at login — you will still log in withsudo mysql. That confuses many beginners. Decide which scheme you want first (see the bottom of this page).
Running the script step by step
First make sure the MySQL service (the background program, also called a daemon) is running, then launch the helper. You must use sudo because it needs admin access.
sudo systemctl status mysql
sudo mysql_secure_installation
Output:
Securing the MySQL server deployment.
Connecting to MySQL using a blank password.
The script now asks a series of questions. Here is each prompt explained.
Prompt 1 — VALIDATE PASSWORD component
VALIDATE PASSWORD COMPONENT can be used to test passwords...
Would you like to setup VALIDATE PASSWORD component? [y/N]
Answer y to enforce password strength rules, then pick a policy level:
There are three levels of password validation policy:
LOW Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary file
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 1
1 (MEDIUM) is a sensible default for most servers. When NOT to enable this: if your root account stays on auth_socket and you only ever create users through automation that already generates strong passwords, the prompts add friction. Otherwise, leave it on.
Prompt 2 — set the root password
Please set the password for root here.
New password:
Re-enter new password:
Type a strong password (it will not show on screen). On a fresh install with auth_socket, the script may instead just confirm the existing setup. Either way, choose a password you store in a secrets manager.
Prompt 3 — remove anonymous users
Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Always answer y. Anonymous users let anyone with network access to the database connect without credentials. There is no legitimate production use for them.
Prompt 4 — disallow remote root login
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Answer y. This forces anyone wanting root to be physically on the server (via SSH first). Remote root over the network is a prime target for attackers. Create dedicated application users for remote connections instead.
Prompt 5 — drop the test database
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
Answer y. The test database is world-writable by default and serves no purpose on a real server.
Prompt 6 — reload privilege tables
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
All done!
Answer y so every change above takes effect at once without restarting MySQL.
Switching root from auth_socket to a password (optional)
If you genuinely need root to log in with a password (for example, a desktop GUI tool that cannot run as the Linux root user), do this manually after the script. First log in:
sudo mysql
Then change the auth method:
ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'YourStrongPassword!23';
FLUSH PRIVILEGES;
EXIT;
Verify it worked from a non-root shell:
mysql -u root -p
Output:
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
mysql>
Best practice on production is to leave
rooton auth_socket and never give it a password. Instead create a separate admin user with a strong password and only the privileges it needs. That way a leaked password can never touch therootaccount.
Confirming the lockdown
You can double-check the result with a couple of SQL queries:
sudo mysql -e "SELECT user, host FROM mysql.user;"
Output:
+------------------+-----------+
| user | host |
+------------------+-----------+
| debian-sys-maint | localhost |
| mysql.infoschema | localhost |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+------------------+-----------+
Notice there are no blank usernames (anonymous users gone) and root is bound only to localhost (no remote root).
Best Practices
- Run
mysql_secure_installationimmediately after install, before creating any real databases or users. - Keep
rooton auth_socket and usesudo mysqlfor admin; create a separate strong-password admin user only if you must. - Always remove anonymous users, drop the
testdatabase, and disallow remoteroot— answeryto all three. - Pair MySQL hardening with the firewall: by default MySQL listens on
127.0.0.1only; if you must expose it, restrict port 3306 withufwto known IPs. - Store every database password in a secrets manager, never in plain text or shell history.
- Re-run the script (or re-check
mysql.user) after any major MySQL upgrade to confirm defaults weren’t reintroduced.