Fix SSH Port Forwarding Failures to a Remote Database
SSH port forwarding lets you securely connect to a remote database that isn’t directly exposed to the internet, by tunneling the connection through an SSH server. When it fails, you can’t reach the database, and the error messages are often cryptic. This guide walks you through the most common causes—wrong flags, binding issues, firewall blocks, and authentication problems—and shows you how to fix each one.
What You’ll Need
- SSH client (OpenSSH is standard on Linux, macOS, and Windows 10+).
- SSH credentials (username and password or key) for the SSH server.
- Network access to the SSH server (usually port 22, but could be custom).
- Database connection details: host, port, and credentials for the database itself.
- Ability to run commands in a terminal or command prompt.
Step-by-Step
- Verify the basic SSH connection. Before dealing with forwarding, ensure you can SSH into the server normally:
ssh user@ssh-server. If this fails, fix the SSH connection first (check hostname, port, credentials, and firewall). - Confirm the database is reachable from the SSH server. Log into the SSH server and try connecting to the database from there using its local tools (e.g.,
mysql -h db-host -P 3306). If that fails, the database is down, unreachable from the server, or the host/port are wrong. Fix that before proceeding. - Use the correct forwarding syntax. For local forwarding (the most common for databases), the syntax is:
ssh -L local_port:db_host:db_port user@ssh-server. For example, to forward local port 3306 to a database atdb.internalon port 3306:ssh -L 3306:db.internal:3306 user@ssh-server. Make sure you don’t mix up the order—the first port is your local port, then the target host and port as seen from the SSH server. - Check for local port conflicts. If the local port is already in use, SSH will fail with “bind: Address already in use.” Choose a different local port, e.g.,
ssh -L 3307:db.internal:3306 ..., and then connect your database client tolocalhost:3307. - Add the
-Nflag if you don’t need a shell. If you only want the tunnel and don’t need an interactive SSH session, usessh -N -L .... This prevents the tunnel from closing when you exit the shell. - Use
-fto background the tunnel. Add-fto run SSH in the background after authentication:ssh -f -N -L 3306:db.internal:3306 user@ssh-server. This frees your terminal. To stop the tunnel later, find the process withps aux | grep sshand kill it. - Test the tunnel. In a new terminal, try connecting to the database using the local port, e.g.,
mysql -h 127.0.0.1 -P 3306 -u dbuser -p. If that works, you’re done. - If using a key, ensure it’s loaded. If you use SSH keys, make sure the private key is available and the public key is installed on the server. Test with
ssh -i /path/to/key user@ssh-serverfirst. - Check SSH server config for AllowTcpForwarding. On the SSH server, the file
/etc/ssh/sshd_configmust haveAllowTcpForwarding yes(it’s usually yes by default). If it’s set tono, the server will reject forwarding attempts. You’ll need admin access to change it and restart sshd. - Try verbose mode for diagnostics. Run
ssh -v -L ...to see detailed logs. Look for lines like “Local forwarding listening on …” or error messages that point to the exact failure.
Common Mistakes
- Using the wrong host in the forwarding spec. The
db_hostmust be resolvable from the SSH server, not from your local machine. If the database is on the same server as SSH, uselocalhostor127.0.0.1. - Forgetting to specify the database port. If the database runs on a non-default port, you must include it. For example, PostgreSQL often uses 5432, not 3306.
- Connecting to the wrong local port. After setting up the tunnel, your database client must connect to
localhost(or127.0.0.1) and the local port you chose, not the remote host/port. - Not using
-Nand then closing the terminal. If you close the SSH session, the tunnel dies. Use-Nand-fto keep it alive in the background. - Firewall blocking the SSH port. Even if SSH works from your network, some firewalls block port 22. Test with a different port if needed.
Tips
- Use
-Cto enable compression if you’re transferring large amounts of data over a slow link. - For repeated use, create an SSH config entry in
~/.ssh/configwith theLocalForwarddirective. Example:Host tunnelthen simply run
HostName ssh-server
User youruser
LocalForward 3306 db.internal:3306ssh tunnel. - If the database is on the same machine as the SSH server, use
localhostas the target host to avoid DNS issues. - Use
ssh -L 0.0.0.0:3306:db.internal:3306to allow other machines on your local network to use the tunnel (bind to all interfaces). Be careful with security. - If you need to forward multiple ports (e.g., for multiple databases), use multiple
-Lflags in one command.
Troubleshooting
“bind: Address already in use” — The local port is taken. Choose a different local port or kill the process using it.
“Permission denied (publickey)” — Authentication failed. Check your username, key path, or try password authentication. If the server only allows keys, make sure your public key is in ~/.ssh/authorized_keys on the server.
“channel 1: open failed: connect failed” — The SSH server cannot connect to the target host/port. Verify the target host is reachable from the SSH server (try telnet db_host db_port from the server). Also check that the database is listening on that port.
“Warning: remote port forwarding failed for listen port” — This happens with remote forwarding (-R). Usually the remote port is already in use or the server has GatewayPorts disabled. For local forwarding, this message is rare.
Tunnel establishes but database connection times out — The database client may be trying to connect to a different host. Make sure you’re connecting to 127.0.0.1 (not localhost if IPv6 is an issue) and the correct port. Also check if the database server is bound to a specific interface and not accepting connections from the SSH server’s localhost.
SSH connection drops after a while — Add ServerAliveInterval 60 to your SSH command or config to keep the connection alive.
Frequently Asked Questions
Q: What’s the difference between local and remote forwarding?
A: Local forwarding (-L) forwards a port on your local machine to a remote destination. Remote forwarding (-R) forwards a port on the SSH server to a local destination. For accessing a remote database, you almost always want local forwarding.
Q: Can I use SSH port forwarding to access a database over the internet without exposing it?
A: Yes, that’s the main purpose. The database stays behind a firewall, and only the SSH server is exposed. The tunnel encrypts the connection.
Q: How do I stop a background SSH tunnel?
A: Find the process ID with ps aux | grep "ssh -f" and kill it with kill PID. Or use pkill -f "ssh -f -N -L" to kill all matching processes.
Q: Why does my tunnel work from the command line but not from a script?
A: Scripts often run in a non-interactive shell without the SSH agent loaded. Ensure the key is loaded (e.g., ssh-add) or use a key file path with -i.
Q: Can I forward a Unix socket instead of a TCP port?
A: Yes, OpenSSH supports forwarding Unix sockets with -L /local/socket:/remote/socket (with a special syntax). This is less common for databases but possible.
Q: Is it safe to use SSH port forwarding for a production database?
A: Yes, it’s secure because the data is encrypted. However, ensure the SSH server is hardened and the tunnel is only accessible to authorized users. Avoid binding to 0.0.0.0 unless necessary.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.