Symptom
An application or DBA can't connect to a MySQL HeatWave DB system. The error tells you where to look:
| Client error | Most likely cause | Go to |
|---|---|---|
ERROR 2003 (HY000): Can't connect to MySQL server on '10.0.2.15:3306' (110) or a hang | No network path: routing, security list/NSG, wrong IP, DB system not active | Steps 1-3 |
ERROR 2003 ... (111) connection refused | Wrong port, or the DB system is starting, stopping or failing over | Steps 1-2 |
ERROR 2005 (HY000): Unknown MySQL server host | DNS name not resolvable from the client | Step 2 |
ERROR 1045 (28000): Access denied for user | Wrong password, or the account's host part doesn't match | Step 4 |
ERROR 1130 (HY000): Host '10.0.1.20' is not allowed to connect | No account for that user and host | Step 4 |
ERROR 1040 (HY000): Too many connections | max_connections reached | Step 5 |
ERROR 2059 or "Authentication plugin 'caching_sha2_password' cannot be loaded" | Old client or driver | Step 6 |
ERROR 3159 (HY000): Connections using insecure transport are prohibited | Server requires TLS, client connects without it | Step 6 |
Set variables first:
C=ocid1.compartment.oc1..xxxx
DB=ocid1.mysqldbsystem.oc1..xxxx
1. Is the DB system up, and what is its endpoint?
oci mysql db-system list -c $C \
--query "data[].{name:\"display-name\", state:\"lifecycle-state\", id:id}" --output table
oci mysql db-system get --db-system-id $DB \
--query "data.{state:\"lifecycle-state\", ip:\"ip-address\", port:port, xport:\"port-x\", host:\"hostname-label\", subnet:\"subnet-id\", ha:\"is-highly-available\", nsgs:\"nsg-ids\"}"
lifecycle-statemust beACTIVE.INACTIVEmeans it was stopped (by a person or a schedule);UPDATINGcovers shape changes, upgrades and some configuration changes.- Use the endpoint
ip-address. On a high availability DB system that endpoint moves to the new primary on failover, so applications must use it (or its DNS name), never the IP of an underlying instance. - Port
3306is the classic MySQL protocol,33060the X Protocol that MySQL Shell uses by default formysqlx://URIs. Open both if people use MySQL Shell.
Start a stopped DB system:
oci mysql db-system start --db-system-id $DB --wait-for-state ACTIVE
Note
Read replicas have their own endpoints, and a read replica load balancer endpoint if you created one. A reporting tool that "can't connect" may be pointing at a replica that was deleted.
2. Can the client reach the endpoint at all?
The DB system endpoint is a private IP in a VCN subnet. A client outside the VCN needs a path: a peered VCN through a DRG, Site-to-Site VPN or FastConnect, or a Bastion session.
From a host in the client's network, test TCP before you test MySQL:
nc -zv -w 5 10.0.2.15 3306
# or without nc
timeout 5 bash -c '</dev/tcp/10.0.2.15/3306' && echo open || echo blocked
If you connect by name, check it resolves to the endpoint IP. The FQDN is built from the hostname label, the subnet DNS label and the VCN DNS label (<host>.<subnet>.<vcn>.oraclevcn.com):
dig +short mydb.dbsubnet.prodvcn.oraclevcn.com
From on-premises, that name only resolves if your DNS forwards oraclevcn.com (or your private zone) to an OCI resolver listener endpoint.
For a quick admin connection from a laptop, use a Bastion port-forwarding session instead of opening the network:
oci bastion session create-port-forwarding --bastion-id <bastion-ocid> \
--target-private-ip 10.0.2.15 --target-port 3306 \
--ssh-public-key-file ~/.ssh/id_rsa.pub --session-ttl 3600 --wait-for-state SUCCEEDED
ssh -i ~/.ssh/id_rsa -N -L 3306:10.0.2.15:3306 -p 22 \
<session-ocid>@host.bastion.<region>.oci.oraclecloud.com
mysql -h 127.0.0.1 -P 3306 -u admin -p
3. Security lists, NSGs and routes
A TCP timeout with the DB system ACTIVE is almost always a missing rule on one side.
| Where | Rule needed |
|---|---|
| DB system subnet security list, or the NSG attached to the DB system | Ingress TCP 3306 (and 33060) from the client CIDR or client NSG |
| Client subnet security list or NSG | Egress TCP 3306/33060 to the DB subnet (stateful rules handle the return) |
| Route tables | Client subnet routes the DB subnet CIDR via the DRG or LPG if they are in different VCNs, and the DB subnet routes back the same way |
| On-premises firewall | Allows 3306/33060 to the OCI CIDR |
Check the NSG rules attached to the DB system:
oci network nsg rules list --nsg-id <nsg-ocid> \
--query "data[?direction=='INGRESS'].{src:source, proto:protocol, ports:\"tcp-options\".\"destination-port-range\"}" \
--output table
Run Network Path Analyzer in the Console (Networking → Network Command Center) from the client IP to the endpoint on port 3306. It shows which security rule or route table drops the traffic.
Warning
Don't fix a timeout by opening 3306 to
0.0.0.0/0on the DB subnet. Allow the application subnet or NSG only, and use Bastion for people.
4. Access denied and host not allowed
Connect as the administrator and look at the account:
SELECT user, host, plugin, account_locked, password_expired
FROM mysql.user
WHERE user = 'app_user';
ERROR 1130means no row matches the client's address. An account created as'app_user'@'10.0.1.%'does not match a client that now connects from10.0.5.20through a new subnet or NAT.ERROR 1045with a matching row means the password is wrong, or a more specific host row with a different password matches first.
Fix the account, scoping the host to the application subnet:
CREATE USER IF NOT EXISTS 'app_user'@'10.0.5.%' IDENTIFIED BY '<strong-password>';
GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app_user'@'10.0.5.%';
ALTER USER 'app_user'@'10.0.5.%' ACCOUNT UNLOCK;
If the administrator password is lost, reset it from OCI without connecting:
oci mysql db-system update --db-system-id $DB --admin-password '<new-password>'
Caution
Passing a password on the command line leaves it in shell history. Run it from Cloud Shell, clear the history afterwards, and check the current docs for whether the change affects open sessions before doing it on production at peak time.
5. Too many connections
SHOW VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SELECT user, SUBSTRING_INDEX(host, ':', 1) AS client, command, COUNT(*) AS sessions
FROM information_schema.processlist
GROUP BY user, client, command
ORDER BY sessions DESC;
The default max_connections depends on the shape; check your configuration's value rather than assuming one. Common causes are a connection pool sized per application instance multiplied by an autoscaled tier, or idle Sleep sessions that are never closed.
Short term: kill idle sessions from the worst offender (KILL <id>;) and lower the pool size. Long term: raise max_connections through a custom configuration attached to the DB system, or move to a larger shape. Changing the configuration can require a restart; plan it.
6. Client, driver and TLS problems
ERROR 2059/caching_sha2_password cannot be loaded: the client library is too old for the default MySQL 8 authentication plugin. Upgrade the connector (JDBC,mysqlclient, PHPmysqlnd) instead of switching the account tomysql_native_password, which is deprecated and disabled by default in 8.4.ERROR 3159: the server's configuration requires TLS. Connect with TLS:
mysql -h 10.0.2.15 -u app_user -p --ssl-mode=REQUIRED
mysqlsh --sql "mysql://app_user@10.0.2.15:3306" --ssl-mode=REQUIRED
Check what the server requires and whether your session uses TLS:
SHOW VARIABLES LIKE 'require_secure_transport';
SHOW SESSION STATUS LIKE 'Ssl_cipher';
An empty Ssl_cipher means the session is not encrypted.
Prevent it next time
- Point applications at the DB system endpoint's DNS name and keep the hostname label stable. Never use instance IPs.
- Put the DB system in its own NSG and reference the application NSG as the source, so subnet changes don't break access.
- Create accounts with host patterns that match the application subnets, and add new subnets to the account list as part of the network change.
- Alarm on the
CurrentConnectionsmetric in theoci_mysql_databasenamespace against yourmax_connections(metric names vary by version; check the namespace in Metrics Explorer). - Keep a Bastion and an admin account documented so an on-call engineer can get a SQL prompt in minutes.