A MySQL GUI with SSH tunnel support is the difference between a safe remote workflow and a database port exposed to the world. If your staging or production MySQL lives on a private network, your desktop client should reach it through SSH — not through a wide-open firewall rule.
What an SSH tunnel does
Instead of:
your Mac → public :3306 → MySQL
you do:
your Mac → SSH (port 22) → bastion/jump host → private MySQL:3306
The GUI opens the SSH session, forwards a local port (or an internal channel), and speaks MySQL through that encrypted path. From the database’s point of view, the connection often appears to come from the bastion.
When you need it
Use SSH tunneling when:
- MySQL listens on a private IP only
- a jump host / bastion is the approved entry point
- your hosting provider blocks direct 3306 from the internet
- you are on untrusted networks (cafés, airports) and still need staging access
Skip tunnels only when you are already on a VPN that makes the DB reachable privately — and even then, least-privilege users still matter.
What the GUI needs from you
Typical SSH tunnel fields:
| Field | Example | Notes |
|---|---|---|
| SSH host | bastion.example.com |
The jump server |
| SSH port | 22 |
Sometimes non-standard |
| SSH user | deploy |
OS user, not MySQL user |
| Auth | private key / password | Keys preferred |
| MySQL host | 10.0.1.40 or mysql.internal |
As seen from the bastion |
| MySQL port | 3306 |
Private port |
| MySQL user/password | app_ro / secret |
Database credentials |
The most common mental error: putting the public website hostname in the MySQL host field while also enabling SSH. After SSH is enabled, the MySQL host should usually be the internal hostname/IP.
Key-based auth tips (macOS / Linux)
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
ssh -i ~/.ssh/id_ed25519 [email protected]
If Terminal SSH works and the GUI does not, compare:
- exact key path
- passphrase prompting
- whether the key is loaded in the agent
- SSH port and username typos
On Windows, keep track of OpenSSH key paths vs PuTTY .ppk formats — some clients accept one and not the other.
Security practices that actually matter
- Disable direct public 3306 once tunnels work
- Use a dedicated SSH user with limited rights on the bastion
- Prefer read-only MySQL users for exploration
- Do not reuse production passwords in local Docker
- Turn off tunnels when done on shared machines
- Log and rotate access for team bastions
An SSH tunnel is not a free pass to run DELETE FROM users without a where clause.
Troubleshooting checklist
SSH succeeds, MySQL fails
- MySQL host/IP is wrong from bastion’s perspective
- security group allows bastion → DB but you used a public IP
- MySQL user is not allowed from the bastion host (
user@bastion-ip)
SSH fails immediately
- key permissions (
chmod 600) - wrong user
- bastion allowlist does not include your current IP
- corporate network blocking outbound 22
Works in CLI SSH tunnel, fails in GUI
Recreate the same mapping manually to compare:
ssh -N -L 3307:10.0.1.40:3306 [email protected]
Then point the GUI at 127.0.0.1:3307 temporarily. If that works, the GUI’s tunnel settings need alignment.
Why this belongs in a desktop client
Browser-based admin tools on the server can bypass your laptop tunnel story — and also expand risk if the web admin is exposed. A local GUI with first-class SSH support keeps credentials and sessions on your machine while still respecting private networking.
TabulaSQL includes SSH tunneling for exactly this workflow: connect to MySQL/MariaDB behind a bastion without publishing the database port.