MySQL Local and Remote Connections
A connection from another host fails twice, with two different errors and two different causes: 2003 because the server is not listening, then 1130 because the account does not exist for that host. Fixing the first does nothing for the second.
Foundations Guide 4 of 45 Intermediate
- OSUbuntu 26.04 LTS (resolute)
- MySQL8.4.10-0ubuntu0.26.04.1
- Packagemysql-server (Ubuntu archive)
- TimeAbout 18 min
- Reviewed27 August 2026
Written against the versions above. `bind-address` lives in `/etc/mysql/mysql.conf.d/mysqld.cnf` on Debian and Ubuntu. On RPM builds the same setting is in `/etc/my.cnf`.
| Server Name | IP Address | OS | Roles | CPU | RAM | HDD |
|---|---|---|---|---|---|---|
| db-a01 | 192.168.0.81 | Ubuntu 26.04 LTS | Primary / Source / Replica Set Member 1 | 2 Core | 4 GB | 50 GB |
| db-util01 | 192.168.0.84 | Ubuntu 26.04 LTS | Client, Backup Target and Monitoring | 2 Core | 4 GB | 50 GB |
Before you start
- MySQL running on db-a01 and the
appuseraccount from guide 2. - A second host that can reach it on the network - db-util01 here.
sudoon both.
-
Read the setting that decides everything
Two views of one value: what the running server has, and what the file says.
bind_addressis127.0.0.1. Ubuntu sets it explicitly on line 31 ofmysqld.cnf, and the running server agrees. This is not a firewall rule and no firewall change will alter it - the server simply never binds to an address another machine could reach.bash Example session sudo mysql --table -e "SHOW VARIABLES LIKE 'bind_address'"+---------------+-----------+| Variable_name | Value |+---------------+-----------+| bind_address | 127.0.0.1 |+---------------+-----------+grep -n "bind-address" /etc/mysql/mysql.conf.d/mysqld.cnf31:bind-address = 127.0.0.132:mysqlx-bind-address = 127.0.0.1Expected result
bind_address= 127.0.0.1, and line 31 of the config setting it.Success conditionYou know why a remote client will fail before you try one.
-
Try root over TCP, and watch socket authentication refuse
-h 127.0.0.1looks like the same machine, and in network terms it is. To MySQL it is a different thing entirely: naming a host makes the client use TCP, where naming nothing (orlocalhost) uses the unix socket.ERROR 1698, not 1045. The account was found and the connection reached the server; the authentication method failed.auth_socketreads the operating system user from the socket itself, and a TCP connection carries no such thing.This is the practical limit of socket authentication: it is unspoofable, and it only works from the local machine.
bash Example session sudo mysql -h 127.0.0.1 -e "SELECT 1" ; echo "exit=$?"ERROR 1698 (28000): Access denied for user 'root'@'localhost'exit=1Expected result
ERROR 1698 (28000): Access denied for user 'root'@'localhost'andexit=1.Success conditionYou have seen that root cannot authenticate over TCP, even locally.
-
Confirm a password account works over both
The same two transports as
appuser, which usescaching_sha2_passwordrather thanauth_socket.Both return
exit=0. A password travels fine over either transport, which is exactly why the account you use from another machine has to be a password account.The literal strings in the results are labels this guide chose, not a report from the server - the proof of which transport was used is the
Connection:line inSTATUS, and the 1698 above.bash Example session mysql -u appuser -p'S7rong-Pass!2026' -h localhost -e "SELECT 'socket path' AS via" ; echo "exit=$?"viasocket pathexit=0mysql -u appuser -p'S7rong-Pass!2026' -h 127.0.0.1 -e "SELECT 'tcp path' AS via" ; echo "exit=$?"viatcp pathexit=0Expected resultBoth succeed with
exit=0.Success conditionYou know a password account is not restricted to one transport.
-
Try it for real from another machine
db-util01 gets the client package - it needs no server - and then attempts the connection that a real application would make.
ERROR 2003 ... (111). The number in brackets is the operating system error: 111 isECONNREFUSED. Nothing was listening. This is a network-layer refusal and the server never saw the attempt - there is no MySQL account involved, no password checked and nothing in the server log.The distinction matters because the fix is a server setting, not a grant.
bash Example session sudo apt-get install -y mysql-clientThe following packages were automatically installed and are no longer required: linux-headers-7.0.0-14 linux-headers-7.0.0-14-generic linux-image-unsigned-7.0.0-14-generic linux-main-modules-zfs-7.0.0-14-generic linux-modules-7.0.0-14-generic linux-tools-7.0.0-14 linux-tools-7.0.0-14-genericUse 'sudo apt autoremove' to remove them.mysql --versionmysql Ver 8.4.10-0ubuntu0.26.04.1 for Linux on x86_64 ((Ubuntu))mysql -u appuser -p'S7rong-Pass!2026' -h 192.168.0.81 -e "SELECT 1" ; echo "exit=$?"ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.0.81:3306' (111)exit=1Expected resultThe client installs, then
ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.0.81:3306' (111)andexit=1.Success conditionYou have reproduced the first failure and know it is a listening problem.
-
Make the server listen on the network
One setting, then a restart -
bind-addressis read at startup and cannot be changed on a running server.Check the
ssoutput carefully. Port 3306 has moved to 0.0.0.0, but 33060 is still on 127.0.0.1:mysqlx-bind-addresson line 32 is a separate setting and was not touched. Two ports, two settings.0.0.0.0means every interface. On a lab network that is fine. On anything reachable from outside, bind to the specific address you mean and put a firewall in front of it.bash Example session grep -n "bind-address" /etc/mysql/mysql.conf.d/mysqld.cnf31:bind-address = 127.0.0.132:mysqlx-bind-address = 127.0.0.1sudo systemctl restart mysqlsudo ss -lntp | grep mysqldLISTEN 0 151 0.0.0.0:3306 0.0.0.0:* users:(("mysqld",pid=8429,fd=23))LISTEN 0 70 127.0.0.1:33060 0.0.0.0:* users:(("mysqld",pid=8429,fd=21))Expected resultLine 31 now
0.0.0.0, line 32 unchanged, and 3306 listening on 0.0.0.0.Success conditionThe server is reachable from the network - at the socket level, at least.
-
Try again, and fail differently
The same command from db-util01 that failed a moment ago.
ERROR 1130, not 2003. This is progress, and reading the change is the skill: the server answered. It accepted the TCP connection, looked for an account matching this client, found none, and said so by name -Host '192.168.0.84' is not allowed.The network problem is solved. What remains is an account problem, and no amount of further
bind-addresswork will touch it. A reader who only ever learned "open bind-address" stops here and starts editing firewall rules.bash Example session mysql -u appuser -p'S7rong-Pass!2026' -h 192.168.0.81 -e "SELECT 1" ; echo "exit=$?"ERROR 1130 (HY000): Host '192.168.0.84' is not allowed to connect to this MySQL serverexit=1Expected result
ERROR 1130 (HY000): Host '192.168.0.84' is not allowed to connect to this MySQL serverandexit=1.Success conditionYou can tell a listening problem from an account problem by the error alone.
-
Create the account for that host, and connect
In MySQL the host is half the account name.
appuser@localhostalready exists and is a different account from the one this client needs.After creating
appuser@192.168.0.84the list shows twoappuserrows. They have separate passwords and separate grants; changing one does not touch the other.The connection then succeeds, and the result is worth reading closely.
CURRENT_USER()isappuser@192.168.0.84- the account the server matched - and@@hostnameisdb-a01, confirming the query really did execute on the other machine.bash Example session sudo mysql -e "CREATE USER 'appuser'@'192.168.0.84' IDENTIFIED BY 'S7rong-Pass!2026'"sudo mysql --table -e "SELECT user, host FROM mysql.user WHERE user='appuser'"+---------+--------------+| user | host |+---------+--------------+| appuser | 192.168.0.84 || appuser | localhost |+---------+--------------+mysql -u appuser -p'S7rong-Pass!2026' -h 192.168.0.81 -e "SELECT CURRENT_USER(), USER(), @@hostname" ; echo "exit=$?"CURRENT_USER() USER() @@hostnameappuser@192.168.0.84 appuser@192.168.0.84 db-a01exit=0Expected resultTwo
appuserrows, thenappuser@192.168.0.84connected and@@hostnamereportingdb-a01.Success conditionA remote client is connected, and you know which of the two failures each fix addressed.
Troubleshooting
ERROR 2003 ... (111)from a remote client.Why: Nothing is listening on that address. Usually
bind-address, occasionally the server being down.Fix:
sudo ss -lntp | grep mysqldon the server. If 3306 is on 127.0.0.1, the firewall is not your problem - changebind-addressand restart.ERROR 1130: Host ... is not allowed to connect.Why: The server is listening and answered. No account exists whose host part matches this client.
Fix:Create the account for that host, or for a pattern like
'10.0.0.%'. Do not reach for'%'by habit - it means every host on earth.ERROR 1698connecting as root to 127.0.0.1 on the server itself.Why:
root@localhostusesauth_socket, which cannot work over TCP.Fix:Drop
-hentirely so the client uses the socket, and usesudo mysql. Use a password account for anything over TCP.3306 moved to 0.0.0.0 but 33060 did not.
Why:
mysqlx-bind-addressis a separate setting for the X protocol port.Fix:Leave it on loopback unless you are using X protocol clients. Two ports, two settings, two decisions.
Official sources
- MySQL 8.4 Reference Manual - mysql_secure_installation
- MySQL 8.4 Reference Manual - The Password Validation Component
- MySQL 8.4 Reference Manual - mysql, the MySQL Command-Line Client
- MySQL 8.4 Reference Manual - Connecting to the MySQL Server
- MySQL 8.4 Reference Manual - Server System Variables (bind_address)