Fix ERROR 2003: Can't connect to MySQL Server on Host (Connection Refused)

intermediate๐Ÿ—„๏ธ MySQL2026-05-31| MySQL 5.7 / 8.0, Ubuntu / Debian / CentOS, remote client connections

Error Message

ERROR 2003 (HY000): Can't connect to MySQL server on 'hostname' (111)
#mysql#connection#remote#firewall#bind-address

The Situation

You're trying to connect to a MySQL server running on another machine โ€” maybe a staging server, a cloud VM, or a dedicated DB host. You run:

mysql -h 192.168.1.100 -u myuser -p

And immediately get hit with:

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

The (111) is the key detail. That's the Linux errno for ECONNREFUSED โ€” the server actively refused the TCP connection. This is different from a timeout (110), where packets are silently dropped. Something is blocking the connection before MySQL even sees it, or MySQL itself isn't listening on that address.

Diagnose First โ€” Don't Guess

Before touching any config, pinpoint exactly where the connection is failing. Run these from your client machine:

# Can you reach the host at all?
ping 192.168.1.100

# Is port 3306 open and accepting connections?
nc -zv 192.168.1.100 3306
# or
telnet 192.168.1.100 3306

Connection refused from nc points to the firewall or MySQL's bind address. A timeout means the firewall is dropping packets silently. A successful connection means MySQL is reachable โ€” but then you'd see a different error, not 2003.

Cause 1: MySQL Is Only Listening on Localhost

This is the most common cause. By default, MySQL binds to 127.0.0.1, which means it only accepts connections from the same machine. Any remote attempt gets refused immediately.

Check what MySQL is actually listening on โ€” run this on the DB server:

sudo ss -tlnp | grep 3306
# or older systems:
sudo netstat -tlnp | grep 3306

127.0.0.1:3306 means MySQL is only accepting local connections โ€” that's your culprit. 0.0.0.0:3306 means it's already listening on all interfaces, so skip to Cause 2.

Fix: Change bind-address in my.cnf

Find your MySQL config โ€” usually /etc/mysql/mysql.conf.d/mysqld.cnf on Ubuntu/Debian, or /etc/my.cnf on CentOS/RHEL:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Find the bind-address line and change it:

[mysqld]
# Before:
bind-address = 127.0.0.1

# After (listen on all interfaces):
bind-address = 0.0.0.0

Prefer listening on a specific LAN IP rather than everything? Use that instead:

bind-address = 192.168.1.100

Restart MySQL to apply the change:

sudo systemctl restart mysql
# or on older systems:
sudo service mysql restart

Verify MySQL is now listening on the right address:

sudo ss -tlnp | grep 3306
# Should show: 0.0.0.0:3306 or your specific IP

Cause 2: Firewall Blocking Port 3306

MySQL can be listening on all interfaces and still be unreachable. The OS firewall or a cloud security group might be dropping inbound traffic on port 3306 before it arrives.

Fix on Ubuntu/Debian (UFW)

# Allow from a specific IP (recommended):
sudo ufw allow from 192.168.1.50 to any port 3306

# Or allow from a subnet:
sudo ufw allow from 192.168.1.0/24 to any port 3306

# Check status:
sudo ufw status

Fix on CentOS/RHEL (firewalld)

# Allow MySQL port:
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --reload

# Or allow from a specific source:
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.50" port port="3306" protocol="tcp" accept'
sudo firewall-cmd --reload

Fix on iptables directly

sudo iptables -A INPUT -p tcp --dport 3306 -s 192.168.1.0/24 -j ACCEPT
# Make persistent:
sudo iptables-save > /etc/iptables/rules.v4

Running on AWS, GCP, or Azure? Check your security groups / VPC firewall rules too. The OS firewall and the cloud firewall are separate layers โ€” I've wasted 20 minutes fixing UFW only to find the security group still had port 3306 blocked.

Cause 3: MySQL Isn't Running

Worth checking before anything else, honestly:

sudo systemctl status mysql
# or:
sudo systemctl status mysqld

Not running? Start it and enable auto-start on boot:

sudo systemctl start mysql
sudo systemctl enable mysql

If it fails to start, the logs will tell you why:

sudo journalctl -u mysql --no-pager -n 50
# or:
sudo tail -100 /var/log/mysql/error.log

Cause 4: Wrong Port or Hostname

Non-standard ports trip people up more often than you'd think. Check what port MySQL is actually using on the server:

sudo ss -tlnp | grep mysql

Then connect with the explicit port:

mysql -h 192.168.1.100 -P 3307 -u myuser -p

Using a hostname instead of an IP? Verify DNS resolves correctly:

nslookup db.example.com
# or:
dig db.example.com

After Fixing the Connection โ€” Grant Remote Access to the MySQL User

TCP is open, MySQL is listening โ€” but now you get Access denied. MySQL user accounts are tied to the host they connect from. A user created as 'myuser'@'localhost' simply cannot log in remotely, even with the right password.

Log in locally on the DB server and grant remote access:

mysql -u root -p

-- Allow from any IP (fine for dev, risky in prod):
CREATE USER 'myuser'@'%' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON mydb.* TO 'myuser'@'%';
FLUSH PRIVILEGES;

-- Better: restrict to a specific IP:
CREATE USER 'myuser'@'192.168.1.50' IDENTIFIED BY 'your_password';
GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'myuser'@'192.168.1.50';
FLUSH PRIVILEGES;

The '%' wildcard allows connections from any IP. Convenient for local dev. Never use it in production.

Verify the Fix

# From client machine โ€” test TCP connection first:
nc -zv 192.168.1.100 3306
# Expected: Connection to 192.168.1.100 3306 port [tcp/mysql] succeeded!

# Then test MySQL login:
mysql -h 192.168.1.100 -u myuser -p mydb
# Expected: MySQL prompt

TCP passes but MySQL login fails? You're past ERROR 2003 and into auth territory โ€” check user grants as described above.

Security Note โ€” Don't Open Port 3306 to the World

Always scope firewall rules to specific IPs or subnets. Exposing MySQL on 0.0.0.0/0 on a public server is asking for trouble โ€” automated scanners will find port 3306 within minutes and start probing it. If your app servers and DB live in the same VPC, restrict to that subnet. For remote developer access, an SSH tunnel is cleaner and safer:

# SSH tunnel โ€” forwards local port 3307 to remote MySQL
ssh -L 3307:127.0.0.1:3306 user@192.168.1.100 -N -f

# Then connect locally:
mysql -h 127.0.0.1 -P 3307 -u myuser -p

MySQL never needs to be exposed on the network at all.

Tips

When writing firewall rules across multiple subnets, it's easy to miscalculate CIDR ranges. I use ToolCraft's Subnet Calculator to quickly verify that an IP like 192.168.1.50 actually falls within 192.168.1.0/24 before committing the rule. It's browser-based with no data uploaded โ€” handy for a quick sanity check.

Related Error Notes