ByteScope

3306

Port 3306 · MySQL

TCPIANA assignedDatabases

The port a MySQL, MariaDB or Percona server listens on for the classic client protocol — and the one your driver reports it cannot reach.

Find what is using port 3306

Run the line for your platform. Each one names the process holding the port and the address it bound to, which is usually the whole answer.

Check it from outside the machine

`nmap -Pn -p 3306 db.example.com` asks the only question that matters from outside the host, and `-Pn` skips host discovery so a machine that ignores pings still gets scanned. Nmap's own definitions decide what you learn: `open` means an application on the target is listening on that port; `closed` means no application is listening, though the host answered, so it is reachable and something merely is not running; `filtered` means a firewall, filter or other network obstacle is blocking the port so nmap cannot tell whether it is open or closed. Add `--reason` to see the packet that decided it — a RST from a closed port versus nothing at all from a filtered one — and `-sV` to let nmap read the MySQL handshake and report the server version. Scan only hosts you are responsible for.

Why a connection to 3306 fails

What you seeWhat it means
ERROR 2003 (HY000): Can't connect to MySQL server on 'db.example.com:3306' (111)Errno 111 is `ECONNREFUSED` on Linux, 61 on macOS: the packet reached the host and the kernel replied with a reset because no process holds the port. The server is stopped, crashed during startup, or bound to an address that does not include the one you dialled. Run the probe for your platform on the database host — not on the client — and read the bind address rather than just confirming a row exists.
The client hangs for tens of seconds, then reports (110) or a connect timeoutErrno 110 is `ETIMEDOUT`: nothing answered at all. A packet filter is dropping the SYN silently — a cloud security group, an on-host firewall, or a network ACL between you and the database. A refusal is instant; a drop is slow, and the delay itself is the diagnosis. Confirm from outside with `nmap --reason`: `filtered` is the drop, `closed` is the refusal.
[ERROR] Do you already have another mysqld server running on port: 3306 ? / bind: Address already in use`EADDRINUSE`: something already holds the socket, and mysqld exits during startup rather than after. Nine times out of ten it is a second MySQL or MariaDB from a different install path, or a container publishing 3306 on the host. Name the owner with the probe above before killing anything, and if the port frees itself a minute or two later, the previous server left sockets in `TIME_WAIT` and nothing needed killing at all.
bind: Permission denied while starting the server on 3306Not the privileged-port rule — that only covers ports below 1024, and 3306 is well above it. Look instead at a mandatory access control policy (SELinux or AppArmor) refusing the bind for that binary, at a container running without the capability, or at the data directory's ownership stopping the server before it ever reaches the socket. On Windows the same shape appears when the port sits inside a reserved exclusion range: nothing is listening, nothing can bind, and there is no PID to blame.
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock'Port 3306 was never involved. You passed `localhost`, so the client used a Unix domain socket, and the path it tried does not exist — usually because the server is in a container, or a different package puts its socket somewhere else. Use `127.0.0.1` to force TCP, or point `--socket` at the real path.
ERROR 1130 (HY000): Host '203.0.113.9' is not allowed to connect to this MySQL serverGood news: the port, the network and the server are all fine. The connection got far enough for MySQL to look up the account and refuse it. MySQL accounts are `user@host` pairs, so a grant for `app@localhost` rejects the same user arriving over TCP. Nothing in the network needs changing.
ERROR 1045 (28000): Access denied for user 'app'@'10.0.0.5' (using password: YES)Also not a port problem — the handshake completed and authentication was attempted and rejected. Wrong password, or the account uses an authentication plugin the client cannot handle, which is the usual reason an old driver fails against `caching_sha2_password` on MySQL 8.
connect ECONNREFUSED 127.0.0.1:3306 from inside a containerInside a container, `127.0.0.1` is that container — not the host and not the database container. Address the database by its Compose service name, or publish the port and use the host's address. The port number is right; the destination is not.

What runs on port 3306

IANA assigns 3306 to `mysql` for both TCP and UDP, but only the TCP half is ever used: it carries the classic MySQL client/server protocol that every driver speaks, from JDBC and `mysqlclient` to `mysql2` and Go's `go-sql-driver`. MariaDB and Percona Server inherit the same default, so one machine can run only one of them on 3306 at a time — which is why a database container refuses to start next to a Homebrew, APT or XAMPP install that is already up. MySQL's newer X Protocol lives on a different number, 33060, registered separately as `mysqlx`, so a socket line ending in 33060 is not the connection you are chasing. One detail catches almost everybody: with the official client libraries the host name `localhost` does not mean 127.0.0.1. It selects a Unix domain socket and the port is ignored entirely, so adding `-P 3306` to a `localhost` connection changes nothing at all. Write `127.0.0.1`, or pass `--protocol=TCP`, when the thing you actually want to test is the port. And ask the running server what it thinks it is doing — `SHOW VARIABLES LIKE 'port'` and `SHOW VARIABLES LIKE 'bind_address'` — rather than reading a config file it may never have loaded.

Should port 3306 face the internet?

Do not expose 3306 to the internet. Anything that reaches it can attempt authentication as often as it likes, and public scanners find a fresh listener within hours. TLS is not the safety net people assume: the client default `--ssl-mode=PREFERRED` encrypts when the server offers it and continues in plaintext when it does not, and it verifies nothing, so it cannot detect an interceptor. Bind the server to a loopback or private address and check what it actually did with `SHOW VARIABLES LIKE 'bind_address'` — the upstream default is `*`, meaning every interface, while several distribution packages override it in their own config, so neither answer is safe to assume without asking. Reach a remote database through an SSH tunnel, a VPN or a private subnet. If it genuinely must be routable, restrict it by source address at the firewall, require TLS on the accounts rather than hoping for it, and remember that a grant of `user@'%'` accepts the whole internet by definition.

Service
MySQL
Transport
TCP
Registry
IANA assigned
Category
Databases

Tools on this site

All of these run in your browser — nothing is uploaded.

Related ports

Ports people usually end up checking in the same session as 3306.

Frequently asked questions

Why does the command show nothing when the port is clearly in use?

Almost always permissions. lsof and ss list the socket but hide the owning process when it belongs to another user, so a server started by a system account, by a container runtime or by launchd shows up as an anonymous listener until you re-run the command with sudo. On Windows the opposite happens: netstat shows nothing at all, yet a bind still fails, which means the port sits inside a range that Hyper-V, WSL2 or Docker Desktop reserved at boot.

Is it safe to kill whatever is holding the port?

Look at what it is first. An orphaned dev server is safe to kill; a database with a write in flight is not, and neither is a system service that something else depends on. If the owner is a container, stop the container instead — killing the process the host sees only makes the runtime restart it. When the process is something you need, moving your own service to another port is the faster fix.

Why can I reach the port locally but not from another machine?

The listener bound to 127.0.0.1 rather than 0.0.0.0. The probes on every page print the bound address next to the process for exactly this reason: a loopback bind is reachable from the machine itself and from nowhere else, no matter how the firewall is configured. Many frameworks made that the default deliberately, so it is usually a flag rather than a bug. If the address is already 0.0.0.0 and it still fails, the next suspect is a host firewall or a cloud security group.

Does moving a service to an unusual port make it safer?

Barely. Internet-wide scanners sweep the whole port range continuously, so an unusual number buys hours, not safety, and it costs you every colleague who now has to remember it. It does cut the noise in your logs from opportunistic scans of the default port, which is a real if modest benefit. Authentication, a firewall that restricts source addresses, and not exposing the service at all are the measures that actually change the outcome.

Still stuck on port 3306? browse every port in the reference — or go back to the failure message you actually got, above.