Skip to content

Bind MySQL to a private interface

Severity: criticalApplies to: MySQL 8.4 LTSApplies to: MariaDB 11.x / 12.x
The fix/etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
bind-address = 127.0.0.1
mysqlx-bind-address = 127.0.0.1
Terminal window
sudo systemctl restart mysql

Everything else in this cluster hardens a daemon an attacker can still reach. This decides whether they can reach it at all.

An internet-reachable MySQL is a brute-force target with no rate limiting and no account lockout, and a successful login is frequently more than a data breach — the FILE privilege reads and writes files as the mysql user.

There is no single answer to “what is the default”, which is why this page leads with checking rather than setting:

  • MySQL’s own built-in default binds all interfaces.
  • Debian and Ubuntu packages override it to 127.0.0.1 in the shipped config. A packaged install is usually already loopback-only.
  • The official Docker images bind all interfaces, correctly — a container that only accepts loopback is useless to other containers. The exposure there is the published port, not this setting.

So the finding isn’t “the default is wrong”. It’s that somebody changed it, or that you’re running a build where the default was never narrowed. Check.

If nothing connects over TCP — a single-host app talking to a local database, which is extremely common — turn TCP off entirely:

[mysqld]
skip-networking

The server then listens only on the unix socket. There is no port to scan, no port to firewall, and no listener to misconfigure later. It is a strictly stronger position than bind-address = 127.0.0.1, and it costs nothing when every client is local.

Clients then connect via socket, which is the default when you use localhost:

Terminal window
mysql -u app -p appdb # socket
mysql -u app -p -h 127.0.0.1 appdb # TCP — will now fail

Note the MySQL client treats localhost and 127.0.0.1 differently: localhost means the socket, 127.0.0.1 means TCP. That surprises people, and it’s why an app can break on skip-networking even though it’s “connecting to localhost”.

MySQL 8 runs a second listener — the X Protocol on port 33060 — with its own mysqlx-bind-address. Narrowing bind-address and leaving X Protocol on every interface is a common half-fix, because ss output on port 3306 looks correct while 33060 is wide open. Disable it if unused:

[mysqld]
mysqlx = 0

MariaDB has no X Protocol, so this doesn’t apply there.