Fixing the MySQL error "Can't connect through socket"

Published on 17 min read

The socket error has five realistic causes. How to work out in five minutes which one you are dealing with, and why localhost and 127.0.0.1 are not the same thing.

The message always turns up at the worst possible moment: after a reboot, after an update, or when the disk filled up overnight. The wording is almost always the same:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

Two details are hidden in there that many people skim past: the path and the number in brackets. Together they tell you fairly precisely where to look. This article works through the five realistic causes (the service is not running, the wrong socket path in the configuration, the application expecting a different path than the server uses, a permission problem, a full disk), shows the diagnosis in the order that gets you to the answer fastest, and explains the difference between localhost and 127.0.0.1, which on its own resolves around half of all cases.

Reading the error message properly

The client tried to connect through a Unix domain socket, that is through a file in the filesystem and not over the network. The path in quotes is the path that the client expects. Whether the server uses that same path is something the message does not tell you. That is exactly where the problem often sits.

The number at the end is the operating system error code:

CodeMeaningWhat that means in practice
(2)No such file or directoryThe socket file does not exist. Either the service is not running, or the path is wrong.
(13)Permission deniedThe file exists, but the calling user is not allowed to open it.
(111)Connection refusedThe file exists, but nothing is listening on it. The classic leftover after a crash.

The exact wording varies by client and version. These variants all mean the same thing:

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
ERROR 2002 (HY000): Can't connect to local server through socket '/run/mysqld/mysqld.sock' (2)
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

From PHP the same error looks like this, because PHP only passes the bare errno through:

PDOException: SQLSTATE[HY000] [2002] No such file or directory
Warning: mysqli_connect(): (HY000/2002): No such file or directory

One distinction matters here: ERROR 2003 is something else. It names an IP address and a port instead of a path, which is the TCP route. And ERROR 1045 (28000): Access denied for user means the connection was established and only the login failed. We have a separate article for that one: Fixing MySQL "Access denied for user".

localhost or 127.0.0.1: the difference that explains half the cases

MySQL and MariaDB treat localhost as a special case. If the configuration literally says localhost, the client library ignores the hostname completely and connects through the Unix socket. If it says 127.0.0.1, the connection goes over TCP to port 3306. That is not a detail, it is the core of the problem: an application configured with localhost reaches the server through a file whose path it picks up somewhere other than the server does.

So the fastest test for whether a server is running at all is this one:

mysql --protocol=TCP -h 127.0.0.1 -P 3306 -u root -p

If you get a password prompt or an access-denied, the service is running and you have a pure socket problem. If you get ERROR 2003 ... (111), the service is either not running or not listening on TCP.

As a permanent fix, however, switching to 127.0.0.1 is only the second-best option. The socket is faster, it bypasses the network stack, and it is not reachable from outside in the first place. More importantly: in the user table, 'app'@'localhost' does not automatically cover 'app'@'127.0.0.1' as well. Anyone who changes the host in the application and then gets an access-denied has run into exactly that effect. And if you do switch to TCP, check that bind-address has not accidentally been set to 0.0.0.0, leaving the database exposed to the internet. Related reading: Securing MariaDB and MySQL and Setting up the UFW firewall.

Diagnosis in five minutes

Step 1: Is the service running at all?

systemctl status mariadb
systemctl status mysql

On Debian and Ubuntu the unit is called mariadb.service for MariaDB (with mysql.service as an alias) and mysql.service for MySQL. On AlmaLinux, Rocky Linux and RHEL it is mariadb.service and mysqld.service respectively. The interesting line is Active:. If it says active (running), jump straight to step 2. If it says failed (Result: exit-code) or inactive (dead), the cause is waiting in the error log.

Step 2: Which socket path does the server actually use?

You can read this out of the configuration files without a running server. my_print_defaults evaluates the same chain of files as the server itself, including every include. Pass all the groups that could apply in one go, then the command works on any distribution:

my_print_defaults client client-server mysqld mariadbd | grep -i socket

The four group names are not overkill, they are the answer to three pitfalls, each of which produces empty output on its own. my_print_defaults client returns nothing at all on any of the distributions we checked, because every option in 50-client.cnf (or /etc/my.cnf.d/client.cnf) is commented out. On Debian and Ubuntu the socket lives in the [client-server] group of /etc/mysql/mariadb.cnf instead, and shows up there as --socket=/run/mysqld/mysqld.sock. On the Red Hat family that group does not exist, and there mysqld delivers the value --socket=/var/lib/mysql/mysql.sock. And from MariaDB 11.8 onwards, meaning from Debian 13 onwards, the server group in 50-server.cnf is no longer called [mysqld] but [mariadbd]. If you only run my_print_defaults mysqld there, you see empty output and wrongly conclude that your configuration is empty.

Alternatively, if the server is running and you can get in:

mysql -e "SHOW VARIABLES LIKE 'socket'"

And with no login at all, asking the kernel directly which Unix sockets are in use:

ss -lx | grep -i mysql

If the shell answers with ss: command not found, the package is simply missing. On Debian and Ubuntu it is called iproute2, on AlmaLinux, Rocky Linux and Oracle Linux iproute:

apt-get install -y iproute2
dnf install -y iproute

That gives you the path the server really provides. Compare it character by character with the path from the error message. /run/mysqld/mysqld.sock and /var/run/mysqld/mysqld.sock are the same thing on modern systems, because /var/run is a symlink to /run. /var/lib/mysql/mysql.sock and /tmp/mysql.sock are not.

Step 3: Does the file exist, and who owns it?

ls -la /run/mysqld/
ls -la /var/lib/mysql/mysql.sock

What you should see is a file of type s (socket), owned by mysql:mysql, with permissions srwxrwxrwx. The directory above it should be drwxr-xr-x mysql mysql. If the directory /run/mysqld is missing entirely, the server has never started successfully, because it is created at startup.

Step 4: Read the error log

This is where the distributions really part ways, and this is the most common reason why guides on the internet do not help. On Ubuntu with MySQL the server writes to /var/log/mysql/error.log. On Debian with MariaDB, log_error is commented out by default, the directory /var/log/mysql/ does not even exist there, and everything ends up in the journal. So take the line that matches your combination:

System and serverCommand
Debian or Ubuntu, MariaDBjournalctl -u mariadb --no-pager -n 50
Debian or Ubuntu, MySQLtail -n 50 /var/log/mysql/error.log
AlmaLinux, Rocky, RHEL, MySQLtail -n 50 /var/log/mysql/mysqld.log
AlmaLinux, Rocky, RHEL, MariaDBtail -n 50 /var/log/mariadb/mariadb.log

One trap deserves special attention, because it produces no error at all: on a Debian system with MariaDB, mysql.service is only an alias for mariadb.service. systemctl resolves the alias, but the journal indexes the entries under the real unit name. journalctl -u mysql answers there with -- No entries -- and exit code 0, even though the journal is full. Anyone who only tries that command assumes the log is empty and keeps searching in the wrong place. The same applies in reverse: on an Ubuntu system with MySQL, journalctl -u mariadb also returns -- No entries --. If you are not sure which unit is meant, query both at once:

journalctl -u 'mysql*' -u 'mariadb*' --no-pager -n 50

If you want a permanent log file, add log_error = /var/log/mysql/error.log to the server group in /etc/mysql/mariadb.conf.d/50-server.cnf on Debian and Ubuntu, create the directory with install -d -o mysql -g mysql /var/log/mysql and restart.

Cause 1: The service is not running (and why)

Reaching for systemctl start mariadb is the reflex, but if the service has just died on its own, it will usually run straight back into the same error. It pays to look at three specific patterns in the log first.

Full disk

InnoDB refuses to start as soon as it cannot write redo logs. Typical lines:

[ERROR] InnoDB: Write to file ./ib_logfile0 failed at offset 0, 1048576 bytes should have been written, only 0 were written
[ERROR] InnoDB: Error number 28 means 'No space left on device'
Can't create/write to file (Errcode: 28 "No space left on device")

Check both, free space and free inodes:

df -h
df -i

A full inode counter on an apparently empty disk happens more often than you would think, usually caused by millions of small session or cache files. What you can safely delete and what you cannot is covered in Cleaning up a full disk on Linux. Never delete anything in /var/lib/mysql by hand, and least of all ib_logfile files while recovery is running.

The OOM killer got there first

If the service disappears in the middle of normal operation without an error message, the kernel has often struck:

dmesg -T | grep -i -E "oom|killed process"
journalctl -k | grep -i oom

A line such as Out of memory: Killed process 1234 (mysqld) is unambiguous. The socket error is only the symptom then. The remedies are a smaller innodb_buffer_pool_size, fewer concurrent PHP workers, or some swap as a buffer, see Setting up swap against out-of-memory.

Orphaned socket file after a crash

If you see this in the log:

[ERROR] Do you already have another mysqld server running on socket: /run/mysqld/mysqld.sock ?
[ERROR] Aborting

then a socket file is lying around with no process behind it. First make sure that no server is really running, then remove the file:

systemctl stop mariadb
pgrep -a mysqld
rm -f /run/mysqld/mysqld.sock
systemctl start mariadb

If pgrep still shows a process, do not delete the file. Otherwise every running application loses its connection, and the server creates a new one at the next start while the old process carries on living.

Cause 2: Server and application mean different paths

This is the case where everything is running and nothing works anyway: ss -lx shows /run/mysqld/mysqld.sock, but the application looks under /tmp/mysql.sock. Typical triggers are self-compiled servers, a switch from MySQL to MariaDB, a move from a panel server to a bare system, or a PHP installation from a third-party repository with different defaults.

The clean way is to line the path up in exactly three places at once. First on the server side. The file has a different name in every combination: on Debian and Ubuntu with MariaDB it is /etc/mysql/mariadb.conf.d/50-server.cnf, on Debian and Ubuntu with MySQL /etc/mysql/mysql.conf.d/mysqld.cnf, on AlmaLinux, Rocky Linux and Oracle Linux /etc/my.cnf.d/mariadb-server.cnf or /etc/my.cnf.d/mysql-server.cnf. A directory /etc/mysql/ does not exist on the Red Hat family at all, everything runs through /etc/my.cnf and /etc/my.cnf.d/ there:

[mysqld]
socket = /run/mysqld/mysqld.sock

Second, for the command-line tools, in 50-client.cnf or a file of your own:

[client]
socket = /run/mysqld/mysqld.sock

Third, for PHP. The three entries in php.ini have to contain the same path, otherwise the server configuration is of no use:

mysqli.default_socket = /run/mysqld/mysqld.sock
pdo_mysql.default_socket = /run/mysqld/mysqld.sock
mysql.default_socket = /run/mysqld/mysqld.sock

Which php.ini applies in the first place is answered by php --ini, provided the package php-cli is installed, otherwise the shell only replies with php: command not found. The caveat behind it matters more: php --ini names the command line file, and that one is almost never the culprit for a web application. The file that counts is the FPM one, usually /etc/php/8.3/fpm/php.ini, and what really arrives there is shown by php-fpm8.3 -i | grep -E 'Loaded Configuration|pdo_mysql.default_socket|mysqli.default_socket'. After the change you need to restart the FPM service, not just reload the web server. If a PHP application still delivers nothing afterwards, the next stop is often Fixing nginx 502 Bad Gateway.

For applications that have the path hard-wired and that you cannot touch, a symlink helps as a last resort:

ln -s /run/mysqld/mysqld.sock /tmp/mysql.sock

That does not survive a reboot, though, because /tmp is cleared on many systems. Permanently this belongs in a tmpfiles rule, or better still in the configuration of the application.

Cause 3: Permissions and a missing /run/mysqld

If you get (13) Permission denied, the socket exists but your user cannot reach it. The socket itself is usually set to 0777, so access fails at the directory above it:

chown mysql:mysql /run/mysqld
chmod 755 /run/mysqld

The second classic: /run is a tmpfs and therefore empty after every reboot. The subdirectory /run/mysqld is recreated at startup, either by systemd-tmpfiles or by the init script. If the matching rule was never shipped with a manual installation, the server starts exactly once (for as long as the directory was there by hand) and never again after the next reboot. In that case create /etc/tmpfiles.d/mysql.conf:

d /run/mysqld 0755 mysql mysql -

You can apply that without a reboot using systemd-tmpfiles --create. If you run a unit of your own, you can set RuntimeDirectory=mysqld instead, see Creating a systemd service.

Cause 4: AppArmor and SELinux

These two produce the most confusing variant of the error, because the filesystem permissions look correct and the server still reports:

[ERROR] Can't start server: Bind on unix socket: Permission denied
[ERROR] Do you already have another mysqld server running on port: 3306 ?

On Ubuntu the MySQL package ships an AppArmor profile. If you have put the socket path somewhere unusual, the profile forbids creating the file. The denials are not in the MySQL log, they are here:

dmesg -T | grep -i apparmor
journalctl -k | grep -i denied

You can extend the profile in /etc/apparmor.d/local/usr.sbin.mysqld, where a line such as /run/mysqld/my.sock rw, goes in, followed by systemctl reload apparmor. On AlmaLinux and Rocky Linux, SELinux is the counterpart, and there you check with ausearch -m avc -ts recent and set the context with semanage fcontext and restorecon. In both cases the more convenient route is to simply leave the socket at its intended default location.

Distribution differences at a glance

Most guides claim there is a single path for all systems. That is not true, and it is exactly why copying someone else's solution fails. As of July 2026:

SystemServerSocketUnitLog
Debian 13MariaDB 11.8/run/mysqld/mysqld.sockmariadbjournalctl
Debian 12MariaDB 10.11/run/mysqld/mysqld.sockmariadbjournalctl
Ubuntu 24.04MySQL 8.0 or MariaDB 10.11/var/run/mysqld/mysqld.sockmysql or mariadb/var/log/mysql/error.log
Ubuntu 22.04MySQL 8.0 or MariaDB 10.6/var/run/mysqld/mysqld.sockmysql or mariadb/var/log/mysql/error.log
AlmaLinux, Rocky, RHELMariaDB or MySQL/var/lib/mysql/mysql.sockmariadb or mysqld/var/log/mariadb/mariadb.log

Two points from that matter. First: Debian does not ship a package mysql-server, MariaDB is the default there. An apt install mysql-server on Debian fails, and the matching guides from the internet lead nowhere. Second: on the Red Hat family the socket sits in the data directory, not under /run. Move an application from Debian to AlmaLinux and take the path with you, and you will reproduce the error reliably.

One special case in passing: containers have neither systemd nor the host's familiar /run/mysqld. If the database runs in a container and the application next to it, there is no shared socket. There is no way around TCP and the container name as the host in that setup.

How to tell that it is really fixed

A systemctl start without an error message is not proof. Check in this order:

systemctl is-active mariadb
ss -lx | grep mysql
mysqladmin ping
mysql -e "SELECT VERSION(), @@socket, @@datadir"

The answer mysqld is alive from mysqladmin ping is the real seal of approval, because it comes back over the same socket your application uses. After that comes the counter-check from the application layer, so not as root but as the user the web server runs under:

sudo -u www-data mysql -u youruser -p yourdatabase -e "SELECT 1"

And finally the reboot test. A frighteningly large share of socket errors come back after the next reboot, because the repair only took effect at runtime (directory created by hand, symlink in /tmp, service not enabled). So:

systemctl enable mariadb
systemctl is-enabled mariadb

If possible, reboot the server completely once and repeat the four check commands. On a KernelHost root server that takes barely a minute and saves you from repeating the whole exercise at three in the morning.

When the repair itself goes wrong

Three situations in which people regularly get stuck.

The server no longer starts at all after a configuration change. A typo in the .cnf leads to an immediate abort, often with unknown variable. The syntax can be checked without starting the service, but the command for it depends on the server:

mysqld --validate-config --user=mysql
mariadbd --help --verbose | head -40

The first line applies to MySQL 8 only. --validate-config is a pure MySQL option and exists in no MariaDB version, verified from 10.5 through 11.8. MariaDB answers instead with [ERROR] mysqld: unknown option '--validate-config' followed by [ERROR] Aborting, and on the Red Hat family that message does not even appear on the terminal, only in the error log: the command is completely silent there. The --user=mysql is mandatory as well and not decoration, because when called as root, MySQL 8 aborts even earlier with Please consult the Knowledge Base to find out how to run mysqld as root!. For MariaDB there is no counterpart to --validate-config. There the second line shows which options the server knows at all, and my_print_defaults mysqld mariadbd shows what it actually reads from your files.

Keep a copy before every change, then the way back is a single cp. Pay attention as well to which file you write into: on Debian and Ubuntu the files in conf.d are read in alphabetical order, and a later entry overrides an earlier one.

You can no longer get in as root. With MariaDB on Debian and Ubuntu, authentication via unix_socket is the default for root@localhost. That means sudo mysql works without a password, while mysql -u root -p as a normal user does not, and fails with ERROR 1698 (28000): Access denied for user 'root'@'localhost'. That is not a socket problem, it is intended behavior.

You deleted the socket file while the server was running. The process keeps running, holds the deleted inode and is no longer reachable through the path. You cannot recreate the file by hand, a socket only comes into existence through bind() by the process. The only thing that helps here is a clean restart of the service. Until that happens, you can reach the server over TCP with --protocol=TCP, provided skip-networking is not set. Use that window for a dump of the most important databases before you restart.

One final note on the order of things: never change several things at once. Service status first, then the path comparison, then the permissions. If you touch my.cnf, php.ini and the file permissions in parallel, afterwards you will not know what helped, and next time you will be starting from scratch again. If you are setting up a system from scratch and want to avoid pitfalls like these from the beginning, the checklist for a new root server helps, and for the complete database stack with a web interface there is Installing Apache, PHP, MySQL and phpMyAdmin on Debian.

Frequently asked questions

What does the number in brackets at the end of the error message mean?
It is the operating system error code. (2) means "No such file or directory", so the socket file does not exist. (13) means "Permission denied", the file is there but may not be opened. (111) means "Connection refused", the file exists but no process is listening on it, typically a leftover after a crash.
Why does 127.0.0.1 work but localhost does not?
MySQL and MariaDB treat the hostname localhost as a special case and then connect through the Unix socket instead of over the network. With 127.0.0.1, TCP on port 3306 is used. If 127.0.0.1 works, the server is running and the problem lies purely with the socket path or its permissions.
Can I simply switch everything to 127.0.0.1?
As a stopgap yes, as a permanent solution rather not. The socket is faster and not reachable from outside. On top of that, a privilege for 'user'@'localhost' does not automatically apply to 'user'@'127.0.0.1', so you may need an additional GRANT. And the server has to be listening on TCP, which is not the case when skip-networking is set.
Where do I find the error log if /var/log/mysql/error.log does not exist?
With MariaDB on Debian, log_error is commented out by default, so the output ends up in the journal. Use journalctl -u mariadb --no-pager -n 50 and not journalctl -u mysql: mysql.service is only an alias there, the journal indexes under the real unit name, and the alias query silently answers with "-- No entries --". On the Red Hat family the log lives at /var/log/mariadb/mariadb.log (MariaDB) or /var/log/mysql/mysqld.log (MySQL). You can force a dedicated file with log_error in the server group.
Why is the socket path different on AlmaLinux than on Debian?
The distributions ship different defaults. Debian and Ubuntu use /run/mysqld/mysqld.sock or /var/run/mysqld/mysqld.sock, while the Red Hat family puts the socket into the data directory as /var/lib/mysql/mysql.sock. When an application moves between the two worlds, the path has to be adjusted in the application configuration and in php.ini.
Am I allowed to delete the file mysqld.sock?
Only if no server process is definitely running. Check that with pgrep -a mysqld after a systemctl stop. If you delete the file while the server is running, every application loses access, and the path cannot be restored by hand, because a socket can only be created by the process itself.
The server never starts again after a reboot although it ran before. What causes that?
Usually the directory /run/mysqld. /run is a tmpfs and empty after every reboot. If the rule that recreates the directory at startup is missing, the server only starts for as long as the manually created directory exists. The fix is a file /etc/tmpfiles.d/mysql.conf with the entry: d /run/mysqld 0755 mysql mysql -

MySQL MariaDB Troubleshooting Database Linux Debian Ubuntu AlmaLinux