ERROR 2002 (HY000) MySQL Socket Connection Failure: A 5-Minute Cause-by-Cause Fix Guide
You're mid-deploy—or you just got paged at 3 a.m.—and this one line shows up. Your blood runs cold.
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)This is a hands-on playbook: copy-paste the commands from the top down, no guessing, and you'll isolate the cause and recover in five minutes. We skip ERROR 1045 (auth), 1205 (locks), and HikariPool (connection pools). This post is socket-layer only.
1. Read the error first: socket vs. TCP
The key insight: if you don't specify a host, or you use -h localhost, the mysql client tries a Unix domain socket file, not TCP. So this error does not mean "the port is blocked." It means "the socket file is missing or unreadable."
The number in parentheses is the decisive clue.
| Display | errno | Meaning | Direction |
|---|---|---|---|
(2) | ENOENT | File does not exist | Service not running / path mismatch |
(13) | EACCES | File exists, permission denied | Ownership, AppArmor, SELinux |
And a one-line map so you don't mix this up with similar errors:
| Error | Layer | Meaning |
|---|---|---|
ERROR 2002 ... through socket ... (2) | Unix socket | Socket file missing |
ERROR 2003 ... can't connect ... (111) | TCP 3306 | Connecting via port, connection refused |
... through socket ... Connection refused | Socket | File exists, but the server isn't accepting |
2. Cause branch table: narrow candidates in 60 seconds
| # | Cause | Typical symptom |
|---|---|---|
| ① | mysqld not running / crashed | Socket file itself is missing (2) |
| ② | Wrong socket path (my.cnf mismatch) | Service is up, but the client looks in the wrong place |
| ③ | Permissions, ownership, AppArmor | File exists, (13) Permission denied |
| ④ | Environment that must use TCP | Remote / split-container setup |
| ⑤ | No socket inside a Docker container | App container has no mysqld |
| ⑥ | Disk full / stale leftover socket | Crash left a .sock behind; restart fails |
Rule: top to bottom. Skip ①–③ and jump to ④ (TCP workaround) and you may connect today—but the same failure will come back.
3. Diagnostic command set (copy-paste)
(1) Is the service alive?
systemctl status mysql # or mariadb
journalctl -u mysql -n 50 --no-pagerHealthy output shows Active: active (running). If it's dead, you've confirmed ①.
(2) Is it actually answering — ping
mysqladmin ping
# mysqld is alive
mysqladmin -h 127.0.0.1 -P 3306 ping # TCP check(3) Is the socket actually listening?
ss -lx | grep mysql
# u_str LISTEN 0 70 /var/run/mysqld/mysqld.sock 12345 * 0
ss -ltnp | grep 3306If ss -lx shows no socket, the server is not opening one.
(4) Path the client expects vs. path the server created — the core of ②
mysqld --verbose --help | grep '^socket' # path
my_print_defaults mysql mysqld | grep socket # settings file
find / -name 'mysqld.sock' 2>/dev/null # fileIf the three values disagree, you've confirmed ②.
(5) Permissions, AppArmor, disk
ls -la /var/run/mysqld/
aa-status | grep mysqld
dmesg | grep -i denied
df -h /var /tmp4. Prescriptions by cause
① Start the service / recover from a crash
sudo systemctl start mysql && systemctl is-active mysqlIf it crashed, chase [ERROR] lines in the logs.
journalctl -u mysql -n 100 --no-pager | grep -i errormysqld_safe --skip-grant-tables disables authentication, so treat it as a last resort and restart normally immediately after recovery.
② Align the socket path (the most common real cause)
The path [mysqld] creates and the path [client]/[mysql] look for must be identical. /etc/mysql/my.cnf:
[mysqld]
socket = /var/run/mysqld/mysqld.sock
[client]
socket = /var/run/mysqld/mysqld.sock
[mysql]
socket = /var/run/mysqld/mysqld.sockIn a pinch, a symlink works as a temporary bypass (it may vanish on reboot, so the real fix is the config above):
sudo ln -s /tmp/mysql.sock /var/run/mysqld/mysqld.sock③ Permissions, ownership, AppArmor
If you see (13), start with ownership:
sudo chown mysql:mysql /var/run/mysqld && sudo chmod 755 /var/run/mysqldIf AppArmor/SELinux is blocking (default policies on recent distros have gotten stricter):
sudo aa-complain /usr/sbin/mysqld # or file socket path⚠️ chmod 777 mysqld.sock is a shortcut to a security incident. Never do it.
④ Switch to TCP — the critical difference between localhost and 127.0.0.1
This is the heart of the post. localhost uses a Unix socket; 127.0.0.1 uses TCP. If the socket is broken, fall back to TCP.
mysql -h 127.0.0.1 -P 3306 --protocol=TCP -u user -pApplication config follows the same rule.
JDBC: jdbc:mysql://127.0.0.1:3306/db
PHP PDO: mysql:host=127.0.0.1;port=3306;dbname=dbIn PHP, host=localhost makes PDO use the socket—that's a classic ERROR 2002 cause.
⑤ Docker environments
Containers are not a systemd host. There is no mysqld socket inside the app container. Connect over TCP using the service name, not a socket.
# docker-compose: db TCP
mysql -h db -P 3306 -u root -p
# socket volume
# -v /var/run/mysqld:/var/run/mysqld
# ( socket )
docker exec -it mysql mysql -u root -pRootless containers add extra socket-mount permission issues, so prefer a TCP setup.
⑥ Disk full / stale socket
df -h /var /tmp # 100%
sudo rm /var/run/mysqld/mysqld.sock # when socket
sudo systemctl restart mysqlA note from the field
In production, about 70% of ERROR 2002 cases are actually ② (path mismatch) and ④ (localhost vs. 127.0.0.1 mixed). I once spent days chasing a ghost outage where the PHP app used localhost (socket) and a batch script used 127.0.0.1 (TCP)—some things worked, some didn't. The takeaway is simple: the whole team standardizes on one connection method (socket everywhere, or TCP via 127.0.0.1) and this error almost disappears.
4-step checklist & anti-patterns
Memorize the recovery order:
- Is the service up? (
systemctl status) - Is the path right? (
mysqld --verbose --helpvsmy_print_defaults) - Are permissions right? (
ls -la, errno(13)) - Can you fall back to TCP? (
-h 127.0.0.1)
Anti-patterns to avoid:
- ❌ Blind
chmod 777 mysqld.sock— security risk - ❌ Running
--skip-grant-tablesin production without knowing why - ❌ Restarting over and over without deleting a stale socket
- ❌ Mixing
localhostand127.0.0.1and looping on the same error
FAQ
Q. Why does localhost fail while 127.0.0.1 works?
A. localhost connects via a Unix socket file; 127.0.0.1 connects via TCP 3306. If the socket file is broken, only localhost fails and 127.0.0.1 works.
Q. What's the difference between errno (2) and (13)?
A. (2) is ENOENT: the socket file itself is missing (service not running / path mismatch). (13) is EACCES: the file exists but permission is denied (ownership, AppArmor/SELinux).
Q. I get ERROR 2002 from a Docker app container.
A. There is no mysqld socket inside the app container. Connect over TCP with -h db (the DB service name), or volume-mount /var/run/mysqld if you're on the same host.
JSON-LD (FAQPage) snippet:
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{"@type":"Question","name":"Why does localhost fail while 127.0.0.1 works?","acceptedAnswer":{"@type":"Answer","text":"localhost uses a Unix socket; 127.0.0.1 uses TCP 3306. If the socket file is broken, only 127.0.0.1 works."}},
{"@type":"Question","name":"What's the difference between errno (2) and (13)?","acceptedAnswer":{"@type":"Answer","text":"(2) ENOENT means the socket file is missing; (13) EACCES means the file exists but permission is denied."}},
{"@type":"Question","name":"How do I find the socket path?","acceptedAnswer":{"@type":"Answer","text":"Compare mysqld --verbose --help | grep '^socket' with my_print_defaults mysql mysqld | grep socket."}},
{"@type":"Question","name":"What if I get ERROR 2002 in Docker?","acceptedAnswer":{"@type":"Answer","text":"The app container has no socket, so connect over TCP using the DB service name, or volume-mount /var/run/mysqld."}}
]
}Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.