Fixing the MySQL error "Can't connect through socket"
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:
| Code | Meaning | What that means in practice |
|---|---|---|
| (2) | No such file or directory | The socket file does not exist. Either the service is not running, or the path is wrong. |
| (13) | Permission denied | The file exists, but the calling user is not allowed to open it. |
| (111) | Connection refused | The 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 server | Command |
|---|---|
| Debian or Ubuntu, MariaDB | journalctl -u mariadb --no-pager -n 50 |
| Debian or Ubuntu, MySQL | tail -n 50 /var/log/mysql/error.log |
| AlmaLinux, Rocky, RHEL, MySQL | tail -n 50 /var/log/mysql/mysqld.log |
| AlmaLinux, Rocky, RHEL, MariaDB | tail -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:
| System | Server | Socket | Unit | Log |
|---|---|---|---|---|
| Debian 13 | MariaDB 11.8 | /run/mysqld/mysqld.sock | mariadb | journalctl |
| Debian 12 | MariaDB 10.11 | /run/mysqld/mysqld.sock | mariadb | journalctl |
| Ubuntu 24.04 | MySQL 8.0 or MariaDB 10.11 | /var/run/mysqld/mysqld.sock | mysql or mariadb | /var/log/mysql/error.log |
| Ubuntu 22.04 | MySQL 8.0 or MariaDB 10.6 | /var/run/mysqld/mysqld.sock | mysql or mariadb | /var/log/mysql/error.log |
| AlmaLinux, Rocky, RHEL | MariaDB or MySQL | /var/lib/mysql/mysql.sock | mariadb 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?
Why does 127.0.0.1 work but localhost does not?
Can I simply switch everything to 127.0.0.1?
Where do I find the error log if /var/log/mysql/error.log does not exist?
Why is the socket path different on AlmaLinux than on Debian?
Am I allowed to delete the file mysqld.sock?
The server never starts again after a reboot although it ran before. What causes that?
2026 KernelHost GmbH. All rights reserved. This guide is protected by copyright. Republishing it on other websites, in whole, in part or in edited form, is not permitted without our written consent. Quoting with a source credit and a link is expressly welcome.

