Skip to main content

I Wrote a Bash Script to SSH Tunnel Into My Remote PostgreSQL Database

5 min readBy Hamza

How I simplified accessing a PostgreSQL database on shared hosting with a short bash script that handles SSH tunneling, connection checks, and background process management.

SSH tunnelPostgreSQLbash scriptremote databaseport forwardingshared hosting

I recently needed to connect to a PostgreSQL database hosted on Namecheap shared hosting. Shared hosting doesn't expose databases to the outside world — there's no direct connection at all. The only way in was through an SSH tunnel. The script creates a SSH tunnel in the background allowing me to connect to PostgreSQL via PgAdmin.


#!/bin/bash

SERVER="[email protected]"
REMOTE_PORT=5432
LOCAL_PORT=5432
SSH_PORT=21098

echo "Checking for existing tunnel..."

is_tunnel_running() {
    timeout 2 nc -z localhost $LOCAL_PORT 2>/dev/null
}

if is_tunnel_running; then
    echo "Tunnel already running on port $LOCAL_PORT"
    exit 0
fi

echo "Starting tunnel: localhost:$LOCAL_PORT -> $SERVER:$REMOTE_PORT"

ssh -N -L $LOCAL_PORT:127.0.0.1:$REMOTE_PORT $SERVER -p $SSH_PORT -o BatchMode=yes -o ConnectTimeout=10 &
SSH_PID=$!

echo "SSH PID: $SSH_PID"
echo "Waiting for tunnel to establish..."
sleep 3

if ! kill -0 $SSH_PID 2>/dev/null; then
    echo "SSH process died. Check your SSH keys or connection."
    exit 1
fi

echo "Verifying tunnel..."
if ! nc -z localhost $LOCAL_PORT 2>/dev/null; then
    echo "Tunnel failed to establish"
    kill $SSH_PID 2>/dev/null
    exit 1
fi

echo "Success! Tunnel is running in background on port $LOCAL_PORT"
echo "PID: $SSH_PID"
echo "To stop the tunnel, run: kill $SSH_PID"

The flags used are important, for example -N prevents executing remote commands (we only need port forwarding), -L maps my local port 5432 to the remote server's localhost:5432, and -p handles the non-standard SSH port my host uses. BatchMode=yes is the key to making this hands-free — it tells SSH to never prompt for a password and rely entirely on key-based authentication. If your keys aren't set up, the script will tell you the SSH process died and exit. After starting the tunnel, it waits a few seconds, checks the process is alive, and verifies the tunnel actually works by probing the local port with nc. If everything passes, the tunnel stays up in the background until you kill it.