How to Fix “Can’t Connect to MySQL Server Remotely” on Ubuntu and Debian

Featured image illustration for the DevAnswers tutorial: FIX MYSQL REMOTE ACCESS

We may earn a referral fee at no additional cost to you when you shop through the links on this page.

You try to reach a MySQL database from another server or a client like Workbench, and the connection is refused: ERROR 2003 (HY000): Can't connect to MySQL server. By default MySQL only listens to itself, so remote clients never get in. This DevAnswers guide opens it up correctly across the three layers that block remote access, then shows the safer SSH-tunnel alternative. (Need the server first? See installing MySQL on Ubuntu.)

Step 1: Let MySQL Listen Beyond localhost 

The first reason a remote client is refused is bind-address. By default MySQL and MariaDB ship with bind-address = 127.0.0.1, which accepts connections only from the local machine. On Debian and Ubuntu you change this in the server config: /etc/mysql/mysql.conf.d/mysqld.cnf for MySQL, or /etc/mysql/mariadb.conf.d/50-server.cnf for MariaDB.

Terminal
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Find the bind-address line and change it. Setting it to 0.0.0.0 listens on every IPv4 interface; setting it to a specific address limits the server to that one interface, which is tighter. Prefer a specific private IP when the server has one:

mysqld.cnf
# All interfaces:
bind-address = 0.0.0.0
# Tighter, if you have a private IP:
# bind-address = 10.0.0.5

Save the file, then restart the service so the change takes effect:

Terminal
sudo systemctl restart mysql
# MariaDB:
# sudo systemctl restart mariadb

Confirm MySQL is now listening on the external interface rather than just loopback:

Terminal
sudo ss -tlnp | grep 3306
Output
LISTEN 0  151  0.0.0.0:3306  0.0.0.0:*  users:(("mysqld",pid=812,fd=22))

Step 2: Create a User That Can Connect Remotely 

A MySQL account is not just a username; it is a username plus the host the connection comes from. An account defined as 'devans'@'localhost' can only connect from the server itself, so even with the port open a remote login is rejected. Create the user scoped to the client’s IP where you can, log in as an admin and run:

SQL
-- Scope the account to the connecting host where possible
CREATE USER 'devans'@'203.0.113.10' IDENTIFIED BY 'a-long-random-password';  -- replace devans with your app user
GRANT ALL PRIVILEGES ON devans_db.* TO 'devans'@'203.0.113.10';
FLUSH PRIVILEGES;

Use 'devans'@'%' (any host) only when the client IP is not fixed; that account leans entirely on the firewall and a strong password, so create the user with a long random secret (a password generator is ideal for a network-reachable account). FLUSH PRIVILEGES reloads the grant tables so the new access is live immediately. Swap devans_db for your actual database name.

Step 3: Open the Firewall to a Trusted IP 

MySQL listens on TCP port 3306, and the OS firewall has to allow it. Do not open that port to the whole internet: a publicly reachable 3306 is a magnet for brute-force and automated exploitation. With UFW (on Debian, sudo apt install ufw first), allow only the client’s address:

Terminal
sudo ufw allow from 203.0.113.10 to any port 3306

A blanket sudo ufw allow 3306 works but exposes the port to everyone and should be a last resort. One more thing on cloud servers: the provider’s own firewall or security group (DigitalOcean Cloud Firewalls, an AWS security group) sits in front of UFW, so you have to allow 3306 from the trusted IP there as well, or the packets never reach the box. Check the local rule landed:

Terminal
sudo ufw status

The Safer Way: Don’t Expose 3306 at All 

Every step above widens your attack surface. The most secure approach is to not expose MySQL to the network at all: leave bind-address = 127.0.0.1, keep the firewall closed, and reach the database through an SSH tunnel. The tunnel forwards a local port over your existing SSH login, so to MySQL the traffic looks like it came from localhost:

Terminal
ssh -o ExitOnForwardFailure=yes -L 3307:localhost:3306 user@your-server

Leave that running, then point your client at 127.0.0.1:3307 on your own machine (a local port other than 3306 avoids a clash if your own machine already runs MySQL). Nothing is exposed, the connection is encrypted, and you can connect as an ordinary localhost user with no remote grants at all. Because the tunnel rides your normal SSH login, there is also one less open port and standing password to manage, which is why it is the default many teams settle on for day-to-day database access.

Use the SSH tunnel as your default for ad-hoc admin access (Workbench, a laptop). Reserve a directly-open 3306 for fixed application servers, and always lock both the firewall rule and the MySQL user to that server’s IP, as defense in depth.

Test the Connection 

If you opened the server directly in Steps 1-3, connect from the client over TCP using its IP and the remote user you created:

Terminal
mysql -h your-server-ip -u devans -p

If you went the SSH-tunnel route instead, you connect to 127.0.0.1:3307 as one of your existing localhost users (the remote-scoped devans account does not apply, since the tunnel makes the traffic look local), exactly as the tunnel section above describes.

If you still get ERROR 2003 (HY000): Can't connect to MySQL server on '...' (111), the client could not reach the server on port 3306, so a network layer is still blocking: re-check bind-address and the firewall rule, including any cloud security group. That is different from ERROR 2002 (the local Unix-socket error) and from an authentication failure once the connection does open. To isolate a network or firewall block, test the port directly first:

Terminal
nc -zv your-server-ip 3306

Connecting from an application rather than the CLI? The same host, user, and password go straight into your PHP PDO connection string, so once the mysql client connects, the app will too.

On a Windows client without nc, run Test-NetConnection your-server-ip -Port 3306 in PowerShell for the same check. A refused or timed-out port points at bind-address or the firewall; an open port that still fails in mysql points at the user’s host or an access-denied (1045) error instead.

Summary 

“Can’t connect remotely” is almost always one of three layers: MySQL bound to localhost, a user without the right host part, or a closed firewall port. Open exactly what you need, scope the user and the firewall rule to specific IPs, and prefer an SSH tunnel so the database is never exposed in the first place. Line those up and the remote client connects, with ERROR 2003 behind you.

Support DevAnswers
Written, tested and maintained by one developer. Your support keeps these guides accurate and ad-light.
Buy me a coffee

13 replies

Leave a reply

  1. Vince

    I was able to connected to mysql server remotely and then in all of a sudden it stopped working after updates and upgrade. Using this instruction to check the firewall and add rules to firewall helps me back connected again. Thank you very very much

  2. Suharian Ramadi

    thanks. work for me

  3. Tai

    Hi there,
    thanks a lot for this tuto to solve.
    In my case it did not work :/

    I have tried many things already
    My user has all privileges enabled
    SSH is enabled
    UFW is installed and running
    Ports are set with allow in from anywhere on nginx http, mysql and even server firewall
    I have restarted each time after settings actions
    mysql.cnfd is also set with bind address 0.0.0.0

    I’m not sure so far if there is still one or few things I could do to solve this lool
    May I have a few tips or help please?

    Again thanks for all

  4. Jim

    I’m still getting the same error, after # out the bind-address line in every version of mysql.cnf or my.cnf I can find. Confirmed via use status 3306 is set to ALLOW. Kind of perplexed here. I can connect with MySQL workbench from inside my network, but not from outside. Any other thoughts? Using MySql8 and Ubuntu server 20.04.

  5. john

    ufw save me!
    Thanks

  6. Arhakim

    Awesome! It’s work.

    Thank you!

  7. Lakshya Singhal

    Implemented the above steps and the problem was resolved immediately. Thank you so much for the guidance.

  8. Alexander Suvorov

    Thanks. Many “How-To’s” on the web don’t mention the default IP binding thing in MySQL.

  9. MANOEL ALVES BASTOS

    Thanks for all…
    Works Fine!!!

  10. artem

    Still get this
    Can’t connect to MySQLServer on ip adress(10600)

  11. anan

    it’s working now ,thanks your share

  12. pablo

    muy bueno gracias

    1. DevAnswers

      ¡A ti!