Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for MySQL By James Joyner IV · · 9 min read

MySQL Error Guide: 'ERROR 2003 (HY000)' Can't Connect to MySQL Server (10061/111)

Fix MySQL ERROR 2003 Can't connect to MySQL server over TCP: server down, wrong port, firewall, bind-address, and skip-networking causing connection refused.

  • #mysql
  • #troubleshooting
  • #errors
  • #connectivity

Exact Error Message

ERROR 2003 (HY000): Can't connect to MySQL server on '10.0.4.21:3306' (111)

On Windows clients the same condition surfaces as error code 10061:

ERROR 2003 (HY000): Can't connect to MySQL server on 'db.internal' (10061)

What the Error Means

ERROR 2003 is a client-side connection failure raised by the MySQL client library when it attempts a TCP/IP connection to the server and the operating system refuses or never completes the handshake. The number in parentheses is the underlying OS errno: 111 (ECONNREFUSED) on Linux and 10061 (WSAECONNREFUSED) on Windows both mean “connection refused” — nothing is listening on that host and port, or a firewall actively rejected the packet. A 110/10060 (timeout) instead means packets were silently dropped.

This is different from ERROR 2002, which is a failure to connect over a local Unix socket. ERROR 2003 always involves a hostname or IP and a TCP port, so the problem lives somewhere on the network path between client and the mysqld listener.

Common Causes

  • The mysqld process is not running (crashed, stopped, or failed to start).
  • MySQL is listening only on 127.0.0.1 because of bind-address, so remote clients are refused.
  • skip-networking (or skip-grant-tables with networking disabled) is set, so TCP is off entirely.
  • The server listens on a non-default port (not 3306) and the client targets the wrong one.
  • A host firewall (firewalld, ufw, iptables), a cloud security group, or a Kubernetes NetworkPolicy blocks port 3306.
  • DNS resolves the hostname to a stale or wrong IP address.

How to Reproduce the Error

Point a client at a host/port where nothing is listening, or where the server is bound to localhost only:

mysql -h 10.0.4.21 -P 3306 -u appuser -p

If mysqld has bind-address = 127.0.0.1, a remote client gets ERROR 2003 (111) even though a local mysql on the server works. You can confirm the refusal at the TCP layer without the MySQL client:

ss -tlnp | grep 3306

An empty result for the external interface means nothing is accepting remote TCP connections on 3306.

Diagnostic Commands

Confirm the server process is alive and which port/interface it listens on:

systemctl status mysql --no-pager
ss -tlnp | grep mysql

Read the bind configuration and recent startup messages:

journalctl -u mysql --since "30 min ago" --no-pager | tail -n 40

From the database server itself (where local access still works), inspect the effective network settings read-only:

mysql -e "SHOW VARIABLES LIKE 'bind_address';"
mysql -e "SHOW VARIABLES LIKE 'port';"
mysql -e "SHOW VARIABLES LIKE 'skip_networking';"
+--------------+-----------+
| Variable_name| Value     |
+--------------+-----------+
| bind_address | 127.0.0.1 |
+--------------+-----------+

A bind_address of 127.0.0.1 confirms remote TCP clients will be refused. Verify firewall state:

sudo firewall-cmd --list-ports
sudo ufw status

Step-by-Step Resolution

  1. Confirm the server is running. If systemctl status mysql shows inactive or failed, start it and read journalctl -u mysql for the failure reason (out of disk, corrupt config, missing data dir).
  2. Verify the listening port and interface with ss -tlnp. If you only see 127.0.0.1:3306, the server is bound to localhost.
  3. To accept remote connections, set bind-address = 0.0.0.0 (or a specific reachable interface IP) in my.cnf under [mysqld], then restart mysqld. Pair this with strong account host grants — never open the port without authentication controls.
  4. Confirm skip-networking is OFF. If SHOW VARIABLES LIKE 'skip_networking' returns ON, remove skip-networking / skip_networking=ON from the config and restart.
  5. Check the port match. If the server runs on 3307, target it explicitly: mysql -h host -P 3307.
  6. Open the firewall and cloud security group for port 3306 from the client’s source range only. Re-test with ss from the server and mysql from the client.
  7. If DNS is involved, resolve the hostname and connect by IP to rule out a stale record.

Prevention and Best Practices

  • Bind to a specific private interface rather than 0.0.0.0 when possible, and restrict access with security groups scoped to known client subnets.
  • Add a TCP health check (port 3306 reachability) to your monitoring so a refused listener pages you before applications notice.
  • Document the canonical port in connection strings and secrets so clients never drift to the wrong port.
  • Keep firewall rules in version-controlled infrastructure-as-code so port changes are auditable.
  • Use a connection pooler or proxy (ProxySQL) with health checks so a single restarted node does not surface as 2003 to every client at once.
  • ERROR 2002 (HY000): Can't connect to MySQL server through socket — the local Unix-socket equivalent.
  • ERROR 2003 ... (110) / (10060) — connection timed out rather than refused, pointing at silently dropped packets (firewall DROP, wrong route).
  • ERROR 1130 (HY000): Host '...' is not allowed to connect — TCP reached the server, but the account has no grant for that host.
  • ERROR 2013 (HY000): Lost connection to MySQL server during query — connection established then dropped mid-query.

Frequently Asked Questions

Why does localhost work but the IP fail? mysql -h localhost uses the Unix socket, bypassing TCP entirely. Using an IP forces TCP, which is exactly the path ERROR 2003 is reporting as broken.

What is the difference between errno 111 and 10061? They are the same condition — connection refused — on Linux and Windows respectively. Both mean nothing accepted the TCP connection.

Is opening bind-address = 0.0.0.0 safe? Only if the firewall and account host grants restrict who can reach the port. Exposing 3306 to the public internet is a common breach vector; keep it on private networks.

How do I tell refused from filtered? A refused connection returns instantly with errno 111/10061. A filtered (firewall-dropped) connection hangs and eventually times out with 110/10060. For a faster triage of a connectivity spike, the free incident assistant can correlate the layers. See more MySQL guides.

Could the server be up but not done initializing? Yes. During startup mysqld opens the port late; a client connecting in that window gets 2003 until the listener is ready. Check journalctl for the “ready for connections” line.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.