Remote database access makes it possible for application servers, automation jobs, and administrators to connect to a central MySQL or MariaDB instance from another host. It reduces “database sprawl” and keeps state in one place, while still allowing services to run wherever they need to run.

A mysqld-compatible server only accepts remote connections when three layers agree: the daemon is listening on a non-loopback interface (bind-address), the network path allows TCP port 3306 (host firewall and any upstream security group), and a database account exists that matches both username and client origin (accounts are scoped as 'user'@'host').

Exposing port 3306 outside a trusted network is a common way to attract scans, password guessing, and unpleasant surprises. The steps below assume a Linux host with systemd and a Debian/Ubuntu-style configuration under /etc/mysql, using a private IP bind and an allowlist firewall rule to keep the blast radius small.

Remote access is safest on a private network; avoid publishing port 3306 to the public internet without strict allowlisting, TLS, and an additional access layer such as a VPN or SSH tunnel.

Steps to enable remote access to MySQL or MariaDB server:

  1. Identify the server private IP address that should accept remote connections.
    $ ip --brief address show scope global
    ens3             UP             10.10.10.10/24

    Binding to a specific private IP is usually safer than binding to every interface.

  2. Check which address the database currently listens on for TCP port 3306.
    $ sudo ss --listening --numeric --tcp --processes | grep --fixed-strings ':3306'
    LISTEN 0      151        127.0.0.1:3306      0.0.0.0:*    users:(("mysqld",pid=1234,fd=21))
  3. Locate the active configuration line that sets bind-address.
    $ sudo grep --recursive --line-number --extended-regexp '^[[:space:]]*bind-address' /etc/mysql
    /etc/mysql/mysql.conf.d/mysqld.cnf:44:bind-address = 127.0.0.1

    MariaDB commonly uses /etc/mysql/mariadb.conf.d/50-server.cnf for server settings.

  4. Edit the server configuration so bind-address listens on the server private IP.
    $ sudoedit /etc/mysql/mysql.conf.d/mysqld.cnf
    [mysqld]
    bind-address = 10.10.10.10

    Setting bind-address to 0.0.0.0 exposes the database on every interface; combining that with a permissive firewall can enable credential guessing and unauthorized data access.

  5. Restart the database service to apply the listener change.
    $ sudo systemctl restart mysql

    On MariaDB, the unit name is typically mariadb.

  6. Verify the database service is active after the restart.
    $ sudo systemctl status mysql
    ● mysql.service - MySQL Community Server
         Loaded: loaded (/lib/systemd/system/mysql.service; enabled; preset: enabled)
         Active: active (running) since Fri 2025-12-12 20:41:08 UTC; 11s ago
       Main PID: 1234 (mysqld)
         Status: "Server is operational"
    ##### snipped #####
  7. Confirm the server is listening on the intended address for TCP port 3306.
    $ sudo ss --listening --numeric --tcp --processes | grep --fixed-strings ':3306'
    LISTEN 0      151       10.10.10.10:3306     0.0.0.0:*    users:(("mysqld",pid=1234,fd=21))
  8. Provision a least-privilege remote account restricted to the client host.
    $ sudo mysql
    mysql> CREATE USER 'appuser'@'10.10.10.20' IDENTIFIED BY 'Str0ngPassw0rd!';
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'appuser'@'10.10.10.20';
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> SHOW GRANTS FOR 'appuser'@'10.10.10.20';
    +--------------------------------------------------------------------------------------------------------------+
    | Grants for appuser@10.10.10.20                                                                                |
    +--------------------------------------------------------------------------------------------------------------+
    | GRANT USAGE ON *.* TO `appuser`@`10.10.10.20`                                                                 |
    | GRANT SELECT, INSERT, UPDATE, DELETE ON `appdb`.* TO `appuser`@`10.10.10.20`                                 |
    +--------------------------------------------------------------------------------------------------------------+
    2 rows in set (0.00 sec)
    
    mysql> EXIT
    Bye

    Keep the host part specific; '%' allows logins from anywhere that can reach port 3306.

    Require encrypted transport when TLS is configured: ALTER USER 'appuser'@'10.10.10.20' REQUIRE SSL;

  9. Allow inbound TCP port 3306 from the client address in ufw.
    $ sudo ufw allow from 10.10.10.20 to any port 3306 proto tcp
    Rule added
    Rule added (v6)

    Cloud security groups and upstream firewalls must also allow TCP 3306 from the same client IP.

  10. Test a remote connection from the client host using the new account.
    $ mysql --host=10.10.10.10 --port=3306 --user=appuser --password --database=appdb --execute="SELECT CURRENT_USER(), @@hostname, @@port;"
    Enter password:
    CURRENT_USER()           @@hostname   @@port
    appuser@10.10.10.20      db01         3306

    Common failures map to specific fixes: ERROR 2003 usually indicates routing/firewall/listener issues; ERROR 1130 usually indicates the account host ('user'@'host') does not match the client IP; Access denied usually indicates password, privileges, or authentication plugin mismatch.

Discuss the article:

Comment anonymously. Login not required.