Skip to content
ohsudo, Home
Databases

MySQL "ERROR 2003: Can't connect to MySQL server": read the number in brackets

Fix MySQL ERROR 2003 "Can't connect to MySQL server": the number in brackets says if it's a stopped server, bind-address, a firewall or the wrong host in Docker.

Beginner4 min readApplies to MySQL 5.7+ · MySQL 8.x · MariaDB 10.x · Ubuntu · Debian · RHEL / Rocky / AlmaLinux · Windows · Docker

by OhSudo ··

Not sure this is your cause?Find it step by step: Can't connect to the database →

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

Your client tried to open a TCP connection and failed, so MySQL hasn’t checked a user or a password yet. The number in brackets at the end is the operating system’s reason, and it decides where to look:

Code Means Likely cause
111 (Linux), 61 (macOS), 10061 (Windows) Connection refused Nothing is listening there: it’s stopped, or bind-address keeps it on localhost
110 (Linux), 60 (macOS), 10060 (Windows) Timed out A firewall drops the packets, or the address is wrong
113 No route to host A network problem, or a firewall rejecting it
  1. Clientconnects to 10.0.0.5:3306
  2. Firewalldrops it: timed out (110)
  3. mysqldstopped, or bind-address on localhost: refused (111)(breaks here)
The TCP connection failed, so MySQL never checked a user. The number in brackets is the operating system's reason: refused means nothing listens there, timed out means a firewall.

TL;DR

On the database server:

Terminal window
sudo systemctl status mysql # mysqld or mariadb on RHEL
sudo ss -tlnp | grep 3306 # is it listening, and on which address?

127.0.0.1:3306 means it only accepts local connections: change bind-address (section 2). 0.0.0.0:3306 with a timeout from elsewhere means a firewall (section 3).

1. Is the server running?

Terminal window
sudo systemctl status mysql
sudo journalctl -u mysql -n 30 --no-pager

The service is mysql on Ubuntu and Debian, mysqld on RHEL, and often mariadb. If it’s stopped, start it and read why it stopped: a full disk is a usual reason, and no space left on device covers that.

On Windows:

Terminal window
Get-Service *mysql*, *maria*
Test-NetConnection 10.0.0.5 -Port 3306

2. Refused: bind-address

Check the address it listens on:

Terminal window
sudo ss -tlnp | grep 3306
LISTEN 0 151 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=812,fd=23))

127.0.0.1 is the default on Debian and Ubuntu, so connections from other machines are refused. Set the address in the config file:

  • /etc/mysql/mysql.conf.d/mysqld.cnf on Ubuntu and Debian;
  • /etc/mysql/mariadb.conf.d/50-server.cnf for MariaDB there;
  • /etc/my.cnf or /etc/my.cnf.d/ on RHEL.
[mysqld]
bind-address = 10.0.0.5

Use the machine’s private address, or 0.0.0.0 for all of them. Then restart and check again:

Terminal window
sudo systemctl restart mysql
sudo ss -tlnp | grep 3306

If ss shows nothing while the service runs, look for skip_networking in the config: it turns off TCP completely. If the port isn’t 3306, a port setting has changed it:

Terminal window
mysql -e "SHOW VARIABLES WHERE Variable_name IN ('port','bind_address','skip_networking');"

3. Timed out: the firewall

Here the packets get dropped on the way. Test from the client, without MySQL involved:

Terminal window
nc -vz -w 5 10.0.0.5 3306

On the server, open the port only for the machines that need it:

Terminal window
# Ubuntu / Debian
sudo ufw allow from 10.0.0.20 to any port 3306 proto tcp
# RHEL / Rocky / AlmaLinux
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.20" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload

In the cloud, also check the security group or network firewall. They sit outside the machine, so the server never sees those packets. You can practice the difference between the two failures in the terminal, and “Connection timed out” vs “Connection refused” explains it in depth.

Warning

Don’t open 3306 to the whole internet. Bots scan for it around the clock. Use a private network, a VPN or an SSH tunnel: ssh -L 3306:127.0.0.1:3306 me@10.0.0.5, then connect to 127.0.0.1 on your machine. The Port Lookup lists what else shouldn’t be public.

4. Docker

Inside a container, 127.0.0.1 is the container itself. That’s the classic reason for error 111 in a Compose project. Use the service’s name:

services:
app:
environment:
DB_HOST: db # not localhost
db:
image: mysql:8.4

Connecting from your computer instead? The port has to be published, with ports: ["127.0.0.1:3306:3306"]. A database also takes a while to start, so an app that connects right away fails once and then works: add a healthcheck. Connecting to localhost from a container goes through every combination.

5. It connects now: what’s next

Past this error, you might see the next layer:

ERROR 1130 (HY000): Host '10.0.0.20' is not allowed to connect to this MySQL server
ERROR 1045 (28000): Access denied for user 'app'@'10.0.0.20' (using password: YES)

Good news: the network works. Those are about accounts, and an account in MySQL is a user and a host. See MySQL error 1045.

Shell, we have a problem!

Similar, but different: ERROR 2002: Can't connect to local MySQL server through socket is the local connection, without TCP: see the socket article. ERROR 2005: Unknown MySQL server host means the name doesn’t resolve. ERROR 2013: Lost connection to MySQL server means it connected and then dropped. ERROR 1040: Too many connections means the server is reachable and full: see MySQL error 1040. ERROR 1130: Host is not allowed to connect means you reached it, and no account matches your address: see MySQL error 1130. An import that stops with Got a packet bigger than 'max_allowed_packet' bytes connected fine, and sent too much at once.

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles