Bind MySQL to a private interface
/etc/mysql/mysql.conf.d/mysqld.cnf[mysqld]bind-address = 127.0.0.1mysqlx-bind-address = 127.0.0.1sudo systemctl restart mysqlWhy it matters
Section titled “Why it matters”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.
The default depends on who packaged it
Section titled “The default depends on who packaged it”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.1in 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.
skip-networking is stronger
Section titled “skip-networking is stronger”If nothing connects over TCP — a single-host app talking to a local database, which is extremely common — turn TCP off entirely:
[mysqld]skip-networkingThe 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:
mysql -u app -p appdb # socketmysql -u app -p -h 127.0.0.1 appdb # TCP — will now failNote 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”.
Don’t forget mysqlx
Section titled “Don’t forget mysqlx”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 = 0MariaDB has no X Protocol, so this doesn’t apply there.
Related
Section titled “Related”- Authentication plugins — the layer behind this one.
- MySQL exposed to the internet — what this closes.