+(91)70149-37521Subscribe Now

How to Allow MariaDB and MySQL Remote Connections Safely

Configure secure MariaDB and MySQL remote access with a private listener, host-specific account, narrow firewall rules, TLS and practical connection tests.

Allow remote connections MySQL or MariaDB server

To allow MariaDB remote connections safely, make MariaDB listen on the required private interface, create a dedicated database user limited to the client host, permit TCP 3306 only from that host, and test the connection over TLS. The same security model applies to MySQL.

Do not enable a remote 'root'@'%' account or expose port 3306 to the whole internet. The examples below use private IP addresses, a separate application account and only the privileges that account needs.

Editor’s note (13 August 2026): This guide was rewritten to replace its original remote-root approach with host-specific accounts, least-privilege grants, private listener addresses, narrow firewall rules and TLS.
Check Safe setting Verification
Configuration file Ubuntu/Debian MariaDB commonly uses /etc/mysql/mariadb.conf.d/50-server.cnf; MySQL commonly uses /etc/mysql/mysql.conf.d/mysqld.cnf. RHEL-family packages commonly use /etc/my.cnf or /etc/my.cnf.d/. Use mariadbd --help --verbose or mysqld --verbose --help to confirm the files read on your server.
Listener Bind to the database server’s private address instead of every public interface. sudo ss -ltnp | grep ':3306 '
Database account Use 'app_user'@'10.0.20.30', not a wildcard root account. SHOW GRANTS FOR 'app_user'@'10.0.20.30';
Firewall Allow TCP 3306 only from the approved client address or private network. Test from the approved client with nc -vz 10.0.10.20 3306.
Encryption Require TLS for remote credentials and data. Run SHOW STATUS LIKE 'Ssl_cipher'; after connecting.

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

Placeholder warning: 10.0.10.20, 10.0.20.30, app_database, app_user, passwords and certificate paths are examples. Replace every one with the real private addresses and values 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. A restart can interrupt active database connections, so schedule it appropriately on a production server:

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, first confirm that UFW is active and the incoming default policy is deny. An allow rule does not protect the port when UFW is inactive or incoming traffic is allowed by default:

nn

sudo ufw status verbosen# Expected: Status: active and Default: deny (incoming)

nn

Then 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:

# WARNING: opens MySQL/MariaDB to every source allowed by upstream networking.n# Do not run this on an internet-facing server.nsudo 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. Confirm firewalld is active and inspect the complete zone policy with sudo firewall-cmd --state and sudo firewall-cmd --zone=public --list-all; the rich rule is only one layer of the effective firewall policy.

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:

-- WARNING: confirm no legitimate process uses this account and that a testedn-- local or host-specific administrative account exists before continuing.nDROP 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.