Your application crashes. The logs show one line that makes your stomach drop:
ERROR 2003 (HY000): Can’t connect to MySQL server on ‘localhost’ (111 “Connection refused”)
Or maybe it’s this one:
ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock’ (2)
Either way, your database is unreachable and everything that depends on it — your website, your API, your application — is dead in the water.
Before you start searching through Stack Overflow threads and changing random configuration values, stop. There are exactly five things that cause MySQL connection failures on Linux. Check them in order and you’ll find the problem in minutes.
The 5 Checkpoints
Here’s the systematic approach. Every MySQL “Connection refused” error is caused by one of these five things:
- MySQL isn’t running
- MySQL isn’t listening on the right port or interface
- A firewall is blocking the connection
- The MySQL user doesn’t have permission to connect from your location
- The socket file is missing or misconfigured (local connections only)
Let’s check each one.
Checkpoint 1: Is MySQL Actually Running?
This sounds obvious, but you’d be surprised how often MySQL has silently crashed or failed to start after a reboot.
sudo systemctl status mysql
On some distributions (CentOS, RHEL, Fedora), the service is called mysqld instead:
sudo systemctl status mysqld
You’re looking for “active (running)” in the output. If it says “inactive (dead)” or “failed”, MySQL isn’t running.
Start it:
sudo systemctl start mysql
If it fails to start, check the error log:
sudo journalctl -u mysql -e
Or check the MySQL error log directly:
sudo tail -50 /var/log/mysql/error.log
Common reasons MySQL fails to start:
Disk space is full. MySQL needs space to write data files, logs, and temporary tables. Check with df -h. If your disk is at 100%, free up space before trying to start MySQL.
Another process is using port 3306. Check with sudo ss -tlnp | grep 3306. If another MySQL instance or MariaDB is already running on that port, you need to stop it first.
Corrupted InnoDB files. If MySQL crashes during a write operation, InnoDB data files can become corrupted. The error log will mention “InnoDB: Unable to lock” or “table is marked as crashed.” This requires database repair — a topic for a separate guide.
Wrong permissions on data directory. MySQL’s data directory (usually /var/lib/mysql) must be owned by the mysql user. Check with ls -la /var/lib/mysql. Fix with sudo chown -R mysql:mysql /var/lib/mysql.
Once MySQL is running, move to the next checkpoint.
Checkpoint 2: Is MySQL Listening on the Right Port and Interface?
MySQL is running, but is it actually accepting connections? Check:
sudo ss -tlnp | grep 3306
You should see something like:
LISTEN 0 151 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=1234,fd=22))
Pay attention to the IP address before :3306:
127.0.0.1:3306 — MySQL only accepts connections from localhost. Remote connections will be refused.
0.0.0.0:3306 — MySQL accepts connections from any IP address. Both local and remote connections work.
If you need remote connections, edit the MySQL configuration:
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
On CentOS/RHEL, the file might be at /etc/my.cnf or /etc/mysql/my.cnf.
Find the bind-address line and change it:
# Only local connections (default)
bind-address = 127.0.0.1
# Accept connections from any IP
bind-address = 0.0.0.0
Save and restart MySQL:
sudo systemctl restart mysql
Important security note: Setting bind-address to 0.0.0.0 makes MySQL accessible from the internet if your server has a public IP. Always combine this with proper firewall rules and strong MySQL user passwords. Never expose MySQL to the public internet without additional security measures.
Checkpoint 3: Is a Firewall Blocking the Connection?
MySQL is running and listening, but connections from other machines are refused. The firewall is the likely culprit.
Ubuntu/Debian (UFW):
sudo ufw status
If the firewall is active and port 3306 isn’t listed, add it:
sudo ufw allow 3306/tcp
For more security, allow only specific IPs:
sudo ufw allow from 192.168.1.100 to any port 3306
CentOS/RHEL (firewalld):
sudo firewall-cmd --list-ports
If 3306 isn’t listed:
sudo firewall-cmd --permanent --add-port=3306/tcp
sudo firewall-cmd --reload
Check iptables directly (works on all distributions):
sudo iptables -L -n | grep 3306
If there’s a DROP or REJECT rule for port 3306, that’s your problem. Remove it or add an ACCEPT rule above it.
Cloud server providers (AWS, DigitalOcean, Google Cloud, etc.) have their own firewall rules separate from the OS firewall. Check your cloud provider’s security groups or firewall settings and make sure port 3306 is allowed.
Checkpoint 4: Does the MySQL User Have Permission to Connect?
MySQL has its own user permission system that’s independent of Linux users. A MySQL user can exist but still be restricted to connecting only from specific hosts.
Connect to MySQL locally (this usually works even when remote connections fail):
sudo mysql -u root
Or if root requires a password:
mysql -u root -p
Check existing users and their host permissions:
SELECT Host, User FROM mysql.user;
The output looks like:
+-----------+------------------+
| Host | User |
+-----------+------------------+
| localhost | root |
| localhost | mysql.sys |
| localhost | debian-sys-maint |
+-----------+------------------+
See the problem? All users have localhost as their host. No user can connect from a remote machine.
Create a user for remote access:
CREATE USER 'myapp'@'%' IDENTIFIED BY 'strong_password_here';
GRANT ALL PRIVILEGES ON myappdb.* TO 'myapp'@'%';
FLUSH PRIVILEGES;
The % wildcard means “any host.” For better security, specify the exact IP:
CREATE USER 'myapp'@'192.168.1.100' IDENTIFIED BY 'strong_password_here';
GRANT ALL PRIVILEGES ON myappdb.* TO 'myapp'@'192.168.1.100';
FLUSH PRIVILEGES;
MariaDB 10.4+ note: MariaDB versions 10.4 and later use the unix_socket authentication plugin by default for the root user. This means you can only log in as root if your Linux user is also root. If you’re getting “Access denied” when trying mysql -u root -p, try sudo mysql instead — it authenticates using your OS identity.
Checkpoint 5: Is the Socket File Missing? (Local Connections Only)
If you’re connecting locally and getting:
ERROR 2002: Can’t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock’ (2)
The socket file is missing. This happens when MySQL crashes or isn’t started properly.
Check if the socket file exists:
ls -la /var/run/mysqld/mysqld.sock
If it doesn’t exist, restart MySQL — it creates the socket file on startup:
sudo systemctl restart mysql
If the file still doesn’t appear, check that the socket directory exists and has the right permissions:
sudo mkdir -p /var/run/mysqld
sudo chown mysql:mysql /var/run/mysqld
sudo systemctl restart mysql
Socket path mismatch: Sometimes the MySQL server creates the socket in one location but the client looks for it in another. Check where the server puts it:
sudo grep socket /etc/mysql/mysql.conf.d/mysqld.cnf
And where the client expects it:
mysql --print-defaults | grep socket
If they don’t match, either update the configuration or specify the socket path when connecting:
mysql -u root --socket=/var/run/mysqld/mysqld.sock
The Docker Connection Problem
If your application runs in Docker and MySQL runs on the host (or vice versa), “Connection refused” is almost guaranteed on your first attempt. Docker containers have their own network namespace — localhost inside a container refers to the container itself, not the host machine.
App in Docker, MySQL on host:
Don’t use localhost or 127.0.0.1 as the MySQL host in your app config. Instead:
On Mac/Windows: use host.docker.internal as the MySQL host.
On Linux: use the Docker bridge IP, typically 172.17.0.1. Find it with:
ip addr show docker0
Also make sure MySQL’s bind-address is 0.0.0.0 (not 127.0.0.1) so it accepts connections from the Docker network.
Both in Docker (docker-compose):
Use the service name as the hostname. In your docker-compose.yml:
services:
app:
# your app config
environment:
DB_HOST: db
db:
image: mysql:8.0
# your MySQL config
The hostname db automatically resolves to the MySQL container’s IP within the Docker network.
Quick Diagnostic Cheat Sheet
Run these commands in order to diagnose the problem in under 60 seconds:
# 1. Is MySQL running?
sudo systemctl status mysql
# 2. Is it listening?
sudo ss -tlnp | grep 3306
# 3. Is the firewall blocking?
sudo ufw status # Ubuntu/Debian
sudo firewall-cmd --list-ports # CentOS/RHEL
# 4. Can you connect locally?
sudo mysql -u root
# 5. Does the socket exist?
ls -la /var/run/mysqld/mysqld.sock
Within these five commands, you’ll know exactly where the problem is. Fix that one thing, and your connection works.