keepcalculator

06 Numbers and Powers

SSH Tunnel Started But Database Unreachable? Fixes

Card No. 06·52233 · Updated · Standard formula, shown below

If you started an SSH tunnel and your database is still unreachable, the tunnel itself is almost never the problem—it is usually a bind address, a listening port, a firewall rule, or the database’s own access controls. This article walks through the most common causes and the exact commands to diagnose and fix each one.

How an SSH Tunnel Actually Works

An SSH tunnel (often called SSH port forwarding) creates an encrypted connection between your local machine and a remote server. When you run a command like ssh -L 3307:localhost:3306 user@server, SSH listens on your local port 3307 and forwards any traffic it receives through the SSH connection to the remote server’s port 3306 on localhost as seen from that server.

Key point: the remote end of the tunnel is always localhost (or 127.0.0.1) on the SSH server. If your database is running on a different host or is bound to a different interface, the tunnel won’t reach it. This is the single most overlooked detail.

Check the Tunnel Is Actually Listening

Before touching the database, confirm that the tunnel process is running and listening on the expected local port. On Linux or macOS:

lsof -iTCP:3307 -sTCP:LISTEN

On Windows, use:

netstat -ano | findstr 3307

If nothing shows, the tunnel didn’t start. Common reasons: SSH authentication failed, the remote port is closed, or you accidentally used a different local port than you think. Re-run the SSH command in verbose mode to see what’s happening:

ssh -v -L 3307:localhost:3306 user@server

Database Listening on the Wrong Interface

Most databases by default listen only on 127.0.0.1 (localhost). That is actually good for security—it means the database is not exposed to the network. But if you configured the tunnel to forward to a different host, or if the database is bound to a specific IP that isn’t 127.0.0.1, the tunnel will connect but the database will refuse.

For MySQL, check with:

SHOW VARIABLES LIKE 'bind_address';

If it shows 0.0.0.0, the database listens on all interfaces, which is fine for a tunnel. If it shows a specific IP like 192.168.1.5, then your tunnel must target that IP, not localhost. For PostgreSQL, check listen_addresses in postgresql.conf. The fix is either to change the database config (not recommended just for a tunnel) or to point the tunnel at the correct IP.

Firewall Rules Blocking the Remote Port

Even if the database is listening on localhost, the remote server’s firewall might block connections to the port you’re forwarding to. On the remote server, test locally:

curl -v telnet://localhost:3306

If that fails, the database isn’t listening on localhost. If it succeeds, try connecting to the server’s public IP on that port—if that fails, a firewall (iptables, ufw, security group) is blocking it. Remember: an SSH tunnel still needs the remote port to be reachable from the SSH server itself. The tunnel doesn’t bypass the remote firewall; it just encrypts the traffic.

Database User Host Restrictions

Databases often restrict which hosts a user can connect from. If your tunnel forwards to localhost, the database sees the connection as coming from 127.0.0.1. If your database user is only allowed to connect from 192.168.1.50 or % (any), it will work. But if the user is restricted to a specific IP that isn’t 127.0.0.1, the connection will be rejected with an “Access denied” error.

For MySQL, check the user’s host:

SELECT user, host FROM mysql.user;

If the host is a specific IP, either change it to localhost or % (with caution) or create a new user for tunneled connections. For PostgreSQL, the pg_hba.conf file controls access—make sure there’s a rule allowing connections from 127.0.0.1.

Common Mistakes and How to Avoid Them

  • Using the wrong local port – You start the tunnel on 3307 but your client connects to 3306. Double-check the port numbers.
  • Forgetting to use the tunnel in the client – Many GUI tools have a separate “SSH tunnel” field. If you enter the remote host directly, you bypass the tunnel.
  • Forwarding to the wrong remote host – The syntax is ssh -L localPort:remoteHost:remotePort. If your database is on a different server, you must put that server’s hostname or IP in the middle, not localhost.
  • Killing the tunnel with Ctrl+C – The tunnel ends when the SSH session ends. Use -N to prevent a shell, and consider -f to background it.
  • Not using -N or -f – Without -N, you get a shell and the tunnel dies when you exit. With -f, SSH goes to background after authentication.

How to Debug Step by Step

  1. Start the tunnel with -v to see verbose output.
  2. Test the local port with telnet localhost 3307 – if you get a connection, the tunnel is up.
  3. On the remote server, test telnet localhost 3306 – if that fails, the database isn’t listening.
  4. Check the database’s bind address and user host permissions.
  5. Check the remote firewall for the database port.
  6. If all else fails, try connecting directly from the remote server to the database using the same credentials to isolate the issue.

Frequently Asked Questions

Why does my tunnel work but I get “Connection refused”? The tunnel is up, but the remote port is not accepting connections. This usually means the database isn’t listening on that port or is bound to a different interface. Check the remote server with telnet localhost <port>.

Can I tunnel to a database on a different server than the SSH server? Yes, use the syntax ssh -L localPort:databaseHost:databasePort user@sshServer. The SSH server will forward the traffic to the database host, but the database host must be reachable from the SSH server.

Why does my tunnel work from the command line but not from a GUI tool? GUI tools often have their own SSH tunnel settings. If you enter the remote host directly in the database connection fields, the tool won’t use the tunnel. Configure the tool to connect to localhost:localPort and, if it has an SSH tunnel option, leave it off.

Is it safe to use an SSH tunnel for a production database? Yes, it’s a common practice because it encrypts traffic and avoids exposing the database port to the internet. However, you should still use strong authentication and keep the SSH server patched.

What does the -N flag do? It tells SSH not to execute a remote command. It’s useful for pure port forwarding because you don’t need a shell, and it prevents the tunnel from closing when you exit the shell.

Why do I get “bind: Address already in use”? Another process is already using the local port you chose. Pick a different local port (e.g., 3308) or kill the existing process.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →