SecurityAugust 4, 20265 min read

How to Secure a MySQL Database

An exposed or default-configured database is a breach waiting to happen. Lock down MySQL/MariaDB: bind to localhost, strong users, least privilege and TLS.

NBy Nxeon

Databases hold your most sensitive data, yet they're routinely left with default settings, weak passwords, or โ€” worst of all โ€” exposed to the whole internet. This guide walks through securing MySQL or MariaDB on a VPS, from the built-in hardening script to network isolation and least-privilege users.

Step 1: run the secure installation script

After installing MySQL/MariaDB, run the built-in hardening wizard:

sudo mysql_secure_installation

Answer the prompts to:

  • Set a strong root password
  • Remove anonymous users
  • Disallow root login remotely
  • Remove the test database
  • Reload privilege tables

This alone closes the most common misconfigurations.

Step 2: bind to localhost โ€” this is the big one

By far the most common database breach is a server listening on 0.0.0.0 (all interfaces), reachable from the internet. Unless you have a specific reason, MySQL should only accept local connections. Edit /etc/mysql/mysql.conf.d/mysqld.cnf (or /etc/mysql/mariadb.conf.d/50-server.cnf):

bind-address = 127.0.0.1

Restart:

sudo systemctl restart mysql

Verify nothing external is listening on 3306:

sudo ss -tlnp | grep 3306

It should show 127.0.0.1:3306, not 0.0.0.0:3306.

{{SCREENSHOT}}

Step 3: create least-privilege users

Never let your app connect as root. Create a dedicated user with rights to only its own database:

CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;

Only grant ALL PRIVILEGES when the app genuinely needs schema changes. Generate strong passwords โ€” see how to generate a strong password.

Step 4: if you must allow remote access, do it safely

Sometimes an app server and database live on different machines. In that case:

  1. Bind to the private network interface, not the public one.
  2. Restrict access at the firewall to the app server's IP only:
sudo ufw allow from 10.0.0.5 to any port 3306 proto tcp
  1. Require TLS for the connection. See UFW for firewall specifics. Never open 3306 to 0.0.0.0.

Step 5: enable TLS for connections

Modern MySQL/MariaDB can encrypt client connections. Confirm TLS is available:

SHOW VARIABLES LIKE '%ssl%';

Then require it for a sensitive user with REQUIRE SSL on the grant. This matters most for cross-network connections.

Step 6: keep it patched and backed up

Database vulnerabilities get patched regularly โ€” enable automatic security updates. And a secure database you can't restore is still a liability, so set up automated backups with tested restores.

Audit and log to catch problems early

A secure database also tells you when something's wrong. Enable logging of failed connections and, on sensitive systems, of administrative actions. In MySQL/MariaDB you can turn on the general or audit log, though on busy servers the audit plugin (MariaDB's server_audit) is the better tool because it's designed for this and is far less noisy:

INSTALL SONAME 'server_audit';
SET GLOBAL server_audit_logging = ON;

Watch for repeated failed logins โ€” the database equivalent of an SSH brute-force โ€” and for connections from IPs you don't recognise. You can even point fail2ban at the MySQL error log to ban hosts that repeatedly fail authentication.

Don't forget the surrounding server

A database is only as secure as the machine it runs on. If an attacker gets a shell on the server, bind-address won't save you โ€” they're already local. So the database hardening here sits on top of the fundamentals: key-only SSH, a firewall, and a non-root sudo user. Store the database credentials your app uses as managed secrets, never hardcoded in source. And keep the database software itself patched โ€” database engines get security fixes regularly, and an unpatched, internet-exposed database is one of the most reliably exploited things on the internet. Treat "bound to localhost, least-privilege user, patched, backed up" as the non-negotiable baseline for any production database.

FAQ

Is bind-address = 127.0.0.1 enough?

It's the single most important setting โ€” it makes the database unreachable from the internet. Combine it with strong users and a firewall for defence in depth.

MySQL or MariaDB โ€” do the steps differ?

The security steps are essentially identical; MariaDB is a drop-in fork. Config file paths differ slightly (mariadb.conf.d vs mysql.conf.d).

Should my app use the root database user?

Never. Create a dedicated least-privilege user scoped to one database. If that credential leaks, the blast radius is limited.

How do I check if my database is exposed?

Run sudo ss -tlnp | grep 3306. If it shows 0.0.0.0, it's listening on all interfaces โ€” fix the bind-address immediately.

The non-negotiable baseline

If you remember nothing else, these are the settings that separate a secure database from a breach waiting to happen:

  • Run sudo mysql_secure_installation on every new install.
  • Set bind-address = 127.0.0.1 so the database isn't reachable from the internet โ€” verify with sudo ss -tlnp | grep 3306.
  • Create a least-privilege user per application; never let apps connect as root.
  • Restrict any necessary remote access to a specific IP at the firewall, over a private interface, with TLS.
  • Patch the engine regularly and keep tested backups.

The most important single check is that ss output: if port 3306 shows 0.0.0.0, your database is exposed to the entire internet, and fixing the bind address is urgent. Everything else builds defence in depth on top of that foundation. A database that's bound to localhost, patched, using scoped users, and backed up is dramatically harder to compromise than the default-configured installs attackers find every day. Make these checks part of your deployment routine rather than an afterthought โ€” bake the bind-address and least-privilege user into your provisioning scripts so a new database starts secure by default instead of being locked down later. The databases that get breached are almost always the ones where someone meant to harden them "soon" and never did.

A properly locked-down database is bound to localhost, uses least-privilege users, and sits behind a firewall. Run yours on a VPS with full root access โ€” see Nxeon VPS hosting and our security page.

#mysql#mariadb#database#security#vps#seobatch

Deploy your first server in under a minute

Creating an account is free and takes no card details. You pay when you deploy โ€” choose a billing term and pay from your wallet or by card at checkout.