How to connect to a database on a private network
How to connect to a database on a private network
Open a client session against a PostgreSQL or MySQL/MariaDB instance that has no floating IP. The steps cover both connection paths: an application instance already on the private network, and a workstation reaching the subnet through a bastion.
Prerequisites
- CLIOpenStack CLI installed and authenticated (
clouds.yamloropenrcsourced)
Windows: CLI examples use bash. Set up a Linux CLI environment on Windows before proceeding.
- A running database deployed from the Self-Managed PostgreSQL or MySQL/MariaDB Database template
- The
db_name,db_user, anddb_passwordvalues you set when you applied the template - For workstation access: a bastion, VPN, or jump instance with a route into the database subnet. See SSH bastion access into a private subnet
Replace DB_PRIVATE_IP with the database instance's private address, JUMP_HOST with your bastion's reachable address, and YOUR_PRIVATE_KEY_PATH with the key matching the key_name on the deployment. The templates default to Ubuntu 24.04, so the SSH user is ubuntu.
Step 1: Find the private address#
List the instances in the project and read the address from the database row:
openstack server list -c Name -c Networks -c StatusThe database instance shows one address on the private network and no floating IP:
+----------+-------------------------------+--------+
| Name | Networks | Status |
+----------+-------------------------------+--------+
| postgres | postgres-net=192.168.40.12 | ACTIVE |
| app-01 | postgres-net=192.168.40.20 | ACTIVE |
+----------+-------------------------------+--------+Step 2: Confirm the security group allows your source#
Find the group attached to the database instance, then list its rules:
openstack server show postgres -c security_groups
openstack security group rule list SECURITY_GROUP_NAMEThe rule you need shows the engine's port with your source range in the IP Range column: 5432 for PostgreSQL, 3306 for MySQL and MariaDB. If your source address falls outside every listed range, add a rule for it with How to create security group rules. Scope the new rule to the narrowest range that covers the client, such as the application subnet or a single instance address.
Step 3: Connect from an application instance#
An instance on the same private network connects directly. SSH to the application instance, install the client package, and open a session.
For PostgreSQL:
sudo apt update && sudo apt install -y postgresql-client
PGPASSWORD='YOUR_DB_PASSWORD' psql -h DB_PRIVATE_IP -U appuser -d appdb -c "SELECT version();"For MySQL or MariaDB:
sudo apt update && sudo apt install -y mariadb-client
mysql -h DB_PRIVATE_IP -u appuser -p'YOUR_DB_PASSWORD' appdb -e "SELECT VERSION();"A version string in the output confirms the network path, the security group rule, and the account all work.
Step 4: Connect from your workstation through a bastion#
A workstation has no route to the private subnet, so forward a local port through the bastion and point the client at 127.0.0.1.
For PostgreSQL:
ssh -i YOUR_PRIVATE_KEY_PATH -N -L 5432:DB_PRIVATE_IP:5432 ubuntu@JUMP_HOSTFor MySQL or MariaDB:
ssh -i YOUR_PRIVATE_KEY_PATH -N -L 3306:DB_PRIVATE_IP:3306 ubuntu@JUMP_HOSTLeave that session running and connect from a second terminal:
PGPASSWORD='YOUR_DB_PASSWORD' psql -h 127.0.0.1 -U appuser -d appdb
mysql -h 127.0.0.1 -u appuser -p'YOUR_DB_PASSWORD' appdbThe tunnel keeps the engine port off the public internet: the bastion terminates the SSH session, and the database still accepts connections only from inside the subnet.
Step 5: Verify from the application#
Point the application at the private address and confirm it connects with its own credentials rather than yours:
postgresql://appuser:[email protected]:5432/appdb
mysql://appuser:[email protected]:3306/appdbStore the password with the rest of the application's secrets; see How to inject application secrets.
Troubleshoot a refused connection#
| Symptom | Where to look |
|---|---|
| The client hangs, then times out | Security group: no rule allows the source range on the engine port |
Connection refused immediately | The engine is not listening on that address (listen_addresses for PostgreSQL, bind-address for MySQL and MariaDB), or the service is down |
no pg_hba.conf entry for host | PostgreSQL authentication: add a matching pg_hba.conf line and reload the server |
Access denied for user | The MySQL account's host pattern does not cover the client address, or the password is wrong |
| The tunnel command exits at once | Local port already in use; choose another local port and point the client at it |
Next steps#
- Database networking and access: the addressing and access model behind these steps
- How to restore PostgreSQL from a self-managed-postgres backup: a restore drill that uses the same private-subnet access
- SSH bastion access into a private subnet: bastion setup and client configuration
Usage Guidelines
The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.
For the full policy, see Usage Guidelines.