Dynamiq
Connections

SSH Tunnels

Reach databases on private networks by adding an ssh_tunnel block to a database Connection — supported types, config fields, and where to configure it.

Databases often sit inside a private network with no public endpoint. Dynamiq's SQL database connection types accept an optional ssh_tunnel block: instead of connecting to the database host directly, the platform connects to an SSH bastion (jump host) you expose, and routes the database traffic through that tunnel.

Supported connection types

The ssh_tunnel block is available on these Connection types:

TypeConnection type string
PostgreSQLdynamiq.connections.PostgreSQL
MySQLdynamiq.connections.MySQL
ClickHousedynamiq.connections.ClickHouse
Verticadynamiq.connections.Vertica
Trinodynamiq.connections.Trino

For each, ssh_tunnel is a sibling of the regular config fields (host, port, database, …). The database host stays the internal address — the one reachable from the bastion, not from the internet.

Tunnel config fields

hoststringrequired
Hostname or IP of the SSH bastion, reachable from the internet.
portinteger
SSH port on the bastion. Defaults to 22.
userstringrequired
SSH user on the bastion (for example ubuntu).
private_keystring
PEM-encoded private key for the SSH user, pasted as text (-----BEGIN OPENSSH PRIVATE KEY----- …).
private_key_passphrasestring
Passphrase for the private key, if it has one.

Authentication is key-based — there is no SSH password field. Use a dedicated SSH user on the bastion with its own key pair, so you can revoke Dynamiq's access without touching other users.

Create a tunneled Connection via the API

The Connections page form does not currently expose the tunnel fields, so create tunneled project Connections through the management API by including ssh_tunnel in config:

# jq builds the JSON safely, including the multi-line private key
jq -n \
  --arg project_id "$DYNAMIQ_PROJECT_ID" \
  --arg password "$DB_PASSWORD" \
  --rawfile private_key "$HOME/.ssh/dynamiq_bastion" \
  '{
    name: "analytics-postgres",
    project_id: $project_id,
    type: "dynamiq.connections.PostgreSQL",
    config: {
      host: "10.0.12.34",
      port: 5432,
      database: "analytics",
      user: "dynamiq_reader",
      password: $password,
      ssh_tunnel: {
        host: "bastion.example.com",
        port: 22,
        user: "dynamiq",
        private_key: $private_key
      }
    }
  }' | curl -X POST "https://api.getdynamiq.ai/v1/connections" \
  -H "Authorization: Bearer $DYNAMIQ_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d @-
import os
from pathlib import Path

import requests

API = "https://api.getdynamiq.ai"
headers = {"Authorization": f"Bearer {os.environ['DYNAMIQ_ACCESS_KEY']}"}

connection = requests.post(
    f"{API}/v1/connections",
    headers=headers,
    json={
        "name": "analytics-postgres",
        "project_id": os.environ["DYNAMIQ_PROJECT_ID"],
        "type": "dynamiq.connections.PostgreSQL",
        "config": {
            "host": "10.0.12.34",  # internal address, reachable from the bastion
            "port": 5432,
            "database": "analytics",
            "user": "dynamiq_reader",
            "password": os.environ["DB_PASSWORD"],
            "ssh_tunnel": {
                "host": "bastion.example.com",
                "port": 22,
                "user": "dynamiq",
                "private_key": Path.home().joinpath(".ssh/dynamiq_bastion").read_text(),
            },
        },
    },
).json()["data"]

print("Connection id:", connection["id"])

The Connection is active immediately and is picked like any other Connection of its type — for example on a SQL Executor node. Updating the tunnel later is a regular PUT /v1/connections/{connection_id} with the full new config.

The ssh_tunnel block tunnels the database protocol only. SSL/TLS options on the database config (for example PostgreSQL's ssl block or ClickHouse's secure flag) still apply to the database session inside the tunnel and can be combined with it.

SSH tunnels in Chat database connectors

Chat has its own per-user database connectors for PostgreSQL, MySQL, Vertica, ClickHouse, and Trino — and there the tunnel is in the UI. In the connector's connect modal, the SSH tunnel section has a Connect through an SSH bastion toggle; enabling it reveals Host, Port (placeholder 22), User, Private key, and Private key passphrase fields. All four of host, port, user, and private key must be filled for the tunnel to be saved with the connector.

A database connector connect modal with the Connect through an SSH bastion toggle enabled and the SSH host, port, user, and private key fields visible

screenshot: chat-connector-db-ssh-tunnel

Bastion checklist

  • Allow inbound SSH (port 22 or your custom port) on the bastion from the internet, and outbound traffic from the bastion to the database host and port.
  • Create a dedicated SSH user with a dedicated key pair; paste the private key into the Connection and put the public key in the user's authorized_keys.
  • Keep the database user's privileges minimal — read-only if workflows only query.

Next steps

On this page