+(91)70149-37521Subscribe Now

How to Allow Remote Connections in MySQL and MariaDB Safely

To allow remote MySQL connections safely, configure MySQL to listen on the required private interface, create a dedicated account limited to the client host, grant only the necessary database privileges, restrict port 3306 with a firewall, and require an encrypted connection. Do not allow the MySQL root account from all hosts. A rule such as […]

Allow remote connections MySQL or MariaDB server

To allow remote MySQL connections safely, configure MySQL to listen on the required private interface, create a dedicated account limited to the client host, grant only the necessary database privileges, restrict port 3306 with a firewall, and require an encrypted connection.

Do not allow the MySQL root account from all hosts. A rule such as 'root'@'%' combined with an internet-open database port creates unnecessary risk. The examples below use a separate application account and private IP addresses.

Example network used in this guide

  • MySQL server: 10.0.10.20
  • Application server: 10.0.20.30
  • Database: app_database
  • Database account: app_user

Replace every example with the real private addresses and names from your environment.

MariaDB configuration notes

The same security model applies when you need to allow MariaDB remote connections: listen only on the required private interface, create a host-specific account, grant the minimum privileges and restrict TCP 3306 at every firewall.

On Ubuntu and Debian, MariaDB commonly reads server settings from /etc/mysql/mariadb.conf.d/50-server.cnf. Edit the active file and set the server’s private address under [mysqld]:

bind-address = 10.0.10.20

Restart and verify MariaDB:

sudo systemctl restart mariadb
sudo systemctl status mariadb --no-pager
sudo ss -ltnp | grep ':3306 '

Distribution packages can use a different include file. Run mariadbd --help --verbose to identify the option-file locations instead of assuming that one path is correct everywhere.

Before enabling remote MySQL access

Confirm that remote TCP access is really required. When the application and database run on the same server, use localhost or a Unix socket. For occasional administration, an SSH tunnel or private VPN may be safer than exposing port 3306 on a network interface.

Create and test a backup before changing production database configuration. Our guide explains how to back up and restore MySQL with mysqldump.

Step 1: Check the current MySQL listener

sudo ss -ltnp | grep ':3306 '

If MySQL listens only on 127.0.0.1:3306, remote hosts cannot connect. Also inspect the effective server value:

sudo mysql -e "SHOW VARIABLES LIKE 'bind_address';"

Check service health:

sudo systemctl status mysql --no-pager

Step 2: Back up the MySQL configuration

On Ubuntu and Debian, the server configuration commonly includes:

/etc/mysql/mysql.conf.d/mysqld.cnf

Create a backup:

sudo cp -a /etc/mysql/mysql.conf.d/mysqld.cnf 
  /etc/mysql/mysql.conf.d/mysqld.cnf.backup

RHEL-based packages may use files under /etc/my.cnf or /etc/my.cnf.d/. Find active option files with:

mysqld --verbose --help 2>/dev/null | grep -A1 'Default options'

Step 3: Set a narrow bind address

Edit the MySQL server configuration:

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

Under [mysqld], set the database server’s private address:

bind-address = 10.0.10.20

This is safer than 0.0.0.0, which listens on all IPv4 interfaces, including a public interface if one exists. Current MySQL versions can accept multiple addresses, but use only the interfaces required by the design.

Make sure skip-networking is not enabled when TCP connections are needed.

Step 4: Restart MySQL and verify the listener

sudo systemctl restart mysql
sudo systemctl status mysql --no-pager
sudo ss -ltnp | grep ':3306 '

If MySQL fails to start, inspect the logs:

sudo journalctl -u mysql -n 100 --no-pager

A bind failure often means the address is not assigned to the server, another process uses the port, or the configuration contains a syntax problem.

Step 5: Create a host-specific MySQL account

Open a local administrative session:

sudo mysql

Create the remote account for the exact application-server IP:

CREATE USER 'app_user'@'10.0.20.30'
  IDENTIFIED BY 'replace-with-a-long-random-password'
  REQUIRE SSL;

In MySQL, 'app_user'@'localhost' and 'app_user'@'10.0.20.30' are different accounts. The host component limits where the connection may originate.

REQUIRE SSL rejects unencrypted TCP connections for this account. For stronger server identity verification, configure a trusted certificate authority on clients and use VERIFY_IDENTITY.

Step 6: Grant least-privilege database access

GRANT SELECT, INSERT, UPDATE, DELETE
ON app_database.*
TO 'app_user'@'10.0.20.30';

If the application performs schema migrations, it may also require selected privileges such as CREATE, ALTER, or INDEX. Do not grant global *.* access for a normal application.

Check the result:

SHOW GRANTS FOR 'app_user'@'10.0.20.30';

You do not need FLUSH PRIVILEGES after using CREATE USER and GRANT. These statements take effect immediately.

Step 7: Restrict port 3306 with UFW

On Ubuntu or Debian with UFW, allow only the application server:

sudo ufw allow from 10.0.20.30 to any port 3306 proto tcp
sudo ufw status numbered

Do not use this broad rule on an internet-facing server:

sudo ufw allow 3306/tcp

See our UFW firewall guide for rule management and rollback.

Step 8: Restrict port 3306 with firewalld

On RHEL, Rocky Linux, AlmaLinux, Fedora, or CentOS, use a rich rule in the active zone:

sudo firewall-cmd --get-active-zones

sudo firewall-cmd --permanent --zone=public 
  --add-rich-rule='rule family="ipv4" source address="10.0.20.30/32" port port="3306" protocol="tcp" accept'

sudo firewall-cmd --reload
sudo firewall-cmd --zone=public --list-rich-rules

Replace public with the zone assigned to the database interface.

Step 9: Update the cloud firewall

If the database runs on AWS, Google Cloud, Azure, DigitalOcean, or another provider, the cloud firewall or security group must also permit TCP 3306 from the application server’s private address or security identity.

A secure design usually places both systems on a private network. Avoid a source of 0.0.0.0/0. Host firewall rules cannot make up for an overly broad public cloud rule.

Step 10: Test from the approved client

From 10.0.20.30, require encryption:

mysql --host=10.0.10.20 
  --user=app_user 
  --password 
  --ssl-mode=REQUIRED 
  app_database

For production identity verification, provide the trusted CA and match the server certificate name:

mysql --host=db.example.internal 
  --user=app_user 
  --password 
  --ssl-mode=VERIFY_IDENTITY 
  --ssl-ca=/path/to/ca.pem 
  app_database

Inside MySQL, confirm encryption and identity:

SELECT CURRENT_USER();
SHOW STATUS LIKE 'Ssl_cipher';

The cipher value should not be empty for an encrypted connection.

Use an SSH tunnel instead of opening MySQL

For temporary administrator access, keep MySQL bound to localhost and forward a local port:

ssh -N -L 3307:127.0.0.1:3306 admin@database-server

In another local terminal:

mysql --host=127.0.0.1 --port=3307 
  --user=database_admin --password

The database is reached through the authenticated SSH connection. Bind the local forward to 127.0.0.1 so it is not exposed to other computers on your local network.

Why not use root@%?

The percent host wildcard can match connections from any host not matched by a more specific account. Combining it with a powerful root account creates a large attack surface and makes mistakes far more damaging.

Use:

  • A separate account for each application or operational role.
  • A specific source host or controlled network.
  • Privileges limited to the required database and operations.
  • TLS plus network filtering.
  • Local root administration through a protected socket.

Remove unsafe remote root access

First inspect accounts and grants:

SELECT User, Host FROM mysql.user WHERE User = 'root';
SHOW GRANTS FOR 'root'@'%';

After confirming that no legitimate process depends on it and that a safe administrator path exists, remove the remote wildcard account:

DROP USER 'root'@'%';

Do not drop 'root'@'localhost' accidentally. MySQL accounts with the same username but different host values are separate.

Common remote MySQL errors

Connection timed out

Check routing, the cloud firewall, the host firewall, and whether MySQL listens on the intended private address. A timeout usually occurs before MySQL account authentication.

Connection refused

The destination is reachable, but nothing accepts the connection. Check ss -ltnp, service status, bind address, and port.

Host is not allowed to connect

The MySQL account’s host component does not match the real client address. Check NAT, proxies, and the account list. Do not solve it by immediately changing the host to %.

Access denied for user

Check the full user-and-host identity, password, authentication plugin, and grants:

SELECT User, Host, plugin FROM mysql.user WHERE User = 'app_user';
SHOW GRANTS FOR 'app_user'@'10.0.20.30';

Connections using insecure transport are prohibited

The account or server requires encryption. Configure the client with --ssl-mode=REQUIRED or, preferably, a trusted CA and VERIFY_IDENTITY.

MySQL fails after changing bind-address

The address may not exist on the database server. Check ip address show, restore the configuration backup if needed, and read the MySQL journal.

Remote MySQL security checklist

  • Use a private network, VPN, or SSH tunnel.
  • Bind only to the required interface.
  • Never expose a remote wildcard root account.
  • Restrict the MySQL account by source host.
  • Grant only required database privileges.
  • Restrict port 3306 at host and cloud firewalls.
  • Require TLS and verify server identity where possible.
  • Keep MySQL patched and maintain tested backups.
  • Review accounts, grants, logs, and network rules regularly.

For account-management commands, see our guide to managing MySQL databases and users.

Frequently asked questions

Which port does remote MySQL use?

Classic MySQL client connections normally use TCP 3306. The server, operating-system firewall, cloud firewall, and client must all agree on the reachable destination.

Can I bind MySQL to 0.0.0.0?

It makes MySQL listen on every IPv4 interface. Prefer the specific private database address, and always apply narrow firewall and account restrictions.

Is a firewall enough to secure remote MySQL?

No. Use defence in depth: network restriction, host-specific accounts, least privileges, strong authentication, TLS, patching, logging, and backups.

Should I allow root from all hosts?

No. Keep root administration local and create a dedicated remote account with only the permissions and source host required.

Official references

You now know how to allow remote MySQL connections without opening root access to the world. Start with the narrowest listener, account, privilege set, firewall source, and encrypted transport that satisfies the application.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Subscribe to Our Newsletter

Get free how-to tutorials and over 700+ courses. Seo tips, create a wordpress, or learn a new skill.