Skip to content
MySQL Error 2003: Can't Connect to Server Fix

Click to use (opens in a new tab)

MySQL Error 2003: Can't Connect to Server Fix

November 4, 2026 by Chat2DBChat2DB Team

Here is an error you see before MySQL has done anything at all:

ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1:3307' (111)

Error 2003 means the TCP connection failed. The client never reached a MySQL server, so this has nothing to do with your username, password or privileges yet. Those problems produce different errors once the connection succeeds, such as 1045 access denied or 1130 host is not allowed.

That narrows the search to the network path: the host, the port, whether MySQL is listening, and anything in between. The most useful part of the message is the number in parentheses at the end, which is the operating system's error code. It tells you which kind of failure you have, and you can usually skip most of the possible causes.

We reproduced the refused, timeout, DNS, bind_address, skip_networking and Docker failures below on MySQL 8.4.11 in Linux containers, so that output is real. The macOS and Windows codes in the table are the same conditions under different operating system numbers.

Read the Code in Parentheses

CodePlatformMeaningWhere to look
(111)LinuxConnection refusedReached the host, nothing listening on that port
(61)macOSConnection refusedSame as 111
(10061)WindowsConnection refusedSame as 111
(110)LinuxConnection timed outNo reply at all, usually a firewall or wrong address
(10060)WindowsConnection timed outSame as 110
(113)LinuxNo route to hostNetwork routing, or a firewall rejecting the traffic

The split between refused and timed out is the most important one. Refused comes back quickly, which proves the host is reachable and something on it answered with "nobody is here". So you should look at the MySQL service and its port. A timeout means packets disappeared, so look at firewalls, security groups and the IP address.

Here are both, from our test:

ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1:3307' (111)
ERROR 2003 (HY000): Can't connect to MySQL server on '10.255.255.1:3306' (110)

If you see this message instead, the problem is DNS, before any connection was attempted:

ERROR 2005 (HY000): Unknown MySQL server host 'db.invalid' (-2)

Check the hostname for typos, and test it with nslookup or getent hosts.

Step 1: Is MySQL Running?

Start on the server itself:

systemctl status mysql        # or mysqld, depending on the distribution
sudo journalctl -u mysql -n 50

If it is stopped, the last lines of the log say why. A common reason is that the server ran out of disk space or memory, or that a configuration change stopped it from starting. On a laptop with MySQL installed through Homebrew, try brew services list. On Windows, check that the MySQL service is running in services.msc.

If the server fails to start at all, fix that first, since nothing else on this page will help.

Step 2: Is It Listening on the Right Address and Port?

A running server can still refuse you. Check what it is listening on:

sudo ss -ltnp | grep -E '3306|mysqld'

Then ask MySQL for the same information, if you can log in locally:

SELECT @@bind_address, @@port, @@skip_networking;
 @@bind_address  @@port  @@skip_networking
 127.0.0.1       3306    0

Three settings cause most refused connections.

The Port Is Wrong

Our first example used port 3307, where nothing was listening. Check the port in your connection string, and remember that a client with no port uses 3306. Installers sometimes pick 3307 when 3306 is taken, and MariaDB and MySQL may be running side by side on one machine.

bind_address Limits It to Localhost

This is the classic cause of "it works on the server but not from my laptop". Many Linux packages set bind-address = 127.0.0.1, so MySQL accepts only local connections. We started a server this way and connected from another container:

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

From inside the same container, -h127.0.0.1 worked. To accept remote connections, change the setting and restart. On Debian and Ubuntu, look in /etc/mysql/mysql.conf.d/mysqld.cnf:

[mysqld]
bind-address = 0.0.0.0

The default in MySQL itself is *, which also listens on all addresses, as our stock container showed. If you can, bind to one private interface and not to everything, which you can do by giving the private address instead:

bind-address = 10.0.1.15

Opening the listener is only half of the job, since the account must also be allowed to connect from your host. If the error changes to 1130, that is progress, and the host is not allowed guide explains the next step.

skip_networking Turns TCP Off

With skip_networking, MySQL accepts only socket connections, and any TCP attempt is refused, even from the same machine:

SELECT @@skip_networking;
-- 1
ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1:3306' (111)

Look for it in the configuration files, and remove it unless it was set on purpose. Password recovery procedures also enable it, since skip-grant-tables turns it on automatically for safety, so a server left in that state can refuse all TCP connections.

Localhost Is Special

There is a subtle point that explains many confusing tests. On Unix, localhost tells the client to use the socket file, not TCP. So these two commands are different:

mysql -uroot -p -hlocalhost -P3307                    # socket, the port is ignored
mysql -uroot -p -hlocalhost -P3307 --protocol=tcp     # TCP

In our test, the first one connected, even though nothing listened on port 3307, because the port was ignored. The second failed with (111), as did the same thing with -h127.0.0.1:

ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost:3307' (111)

So a successful mysql -hlocalhost test proves little about TCP. Use -h127.0.0.1 when you are testing network access. And if localhost fails with error 2002 and not 2003, the socket is the problem, which the error 2002 guide covers.

Step 3: Check the Path Between Client and Server

If MySQL listens properly and you still fail, especially with a timeout, test the port from the client machine, which separates the network from MySQL:

nc -vz 10.0.1.15 3306

If that fails, the problem is outside MySQL. Work outward from the server:

# On the server: is a local firewall dropping the port?
sudo ufw status
sudo firewall-cmd --list-all
 
# Allow only the address that needs access
sudo ufw allow from 10.0.1.20 to any port 3306 proto tcp

On a cloud host, the cloud firewall is separate from the operating system's, and it is often the cause. Check the AWS security group, the GCP firewall rule, the Azure network security group, or the equivalent. For a managed database, also check that your client's IP is on the allow list, and that a private endpoint is reachable from your network.

Do not open port 3306 to the whole internet. Allow specific addresses, or connect over a VPN or an SSH tunnel:

ssh -N -L 3307:127.0.0.1:3306 user@server
mysql -h127.0.0.1 -P3307 -uroot -p

The tunnel connects to the server's loopback address, so it works even with bind-address = 127.0.0.1.

Docker: The Most Common Modern Cause

Containers add two traps.

127.0.0.1 Means the Current Container

Inside a container, 127.0.0.1 is that container, not the host and not the database container. We ran a client in a second container on the same Docker network:

docker run --rm --network labnet --entrypoint mysql mysql:8.4 -h127.0.0.1 -uroot -ppw
ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1:3306' (111)

Use the service or container name as the host, which Docker resolves on a shared network:

services:
  db:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: pw
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-ppw"]
      interval: 5s
      retries: 20
  app:
    image: my-app
    environment:
      DATABASE_URL: mysql://root:pw@db:3306/app
    depends_on:
      db:
        condition: service_healthy

To reach a database on the host from a container, use host.docker.internal on Docker Desktop. From the host, connect to the published port, and check that the mapping exists with docker port, because a container started without -p has no port on the host.

The Application Starts Before MySQL Is Ready

A new MySQL container needs time to initialize, and it does not accept TCP connections until it finishes. We connected in a loop right after starting a container:

0s: ERROR 2003 (HY000): Can't connect to MySQL server on 'mya:3306' (111)
7s: 1

It took 7 seconds here on a small empty database, and it can take far longer with a restored dataset. An application that connects once on startup will crash during this window. depends_on with service_healthy, as in the file above, makes the app wait. Adding a retry loop in the application is still wise, because the database can also restart while your application is running.

Connection Limits Are Not the Cause

Too many connections gives error 1040, not 2003. Our too many connections guide covers that. A failed connection that returns 2003 never reached the server, so max_connections is not involved.

Diagnostic Order

  1. Read the code: refused, timed out, or DNS?
  2. On the server, confirm MySQL is running, and check the log if it is not.
  3. Use ss -ltnp to confirm the address and port.
  4. Check bind_address and skip_networking.
  5. Test with -h127.0.0.1, not localhost.
  6. Test the port from the client with nc -vz.
  7. Check the operating system firewall, then the cloud firewall.
  8. In Docker, use the service name and wait for a health check.

Save Working Connections

Once it works, keep it working. Chat2DB (opens in a new tab) stores your connections with SSH tunnel and SSL settings, and its connection test tells you straight away whether a change broke the path. You can also try it in the browser at app.chat2db.ai (opens in a new tab). If you are building a connection string, the free JDBC URL Builder (opens in a new tab) helps you get the host and port right.