Post

PostgreSQL Through SSH

Quick Guide Python → PostgreSQL Through SSH

PostgreSQL Through SSH

Quick Guide: Python → PostgreSQL Through SSH

1. Architecture

Your setup is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Windows PC
    │
    ▼
WSL2
    │
    │ SSH tunnel
    │ localhost:15432
    ▼
Ubuntu laptop
    │
    │ localhost:5432
    ▼
PostgreSQL 18.6
    │
    ▼
tompkins database

The important point:

  • PostgreSQL port: 5432
  • SSH tunnel port in WSL2: 15432
  • 15432 is only the local endpoint of the tunnel.

2. Verify PostgreSQL on Ubuntu

On Ubuntu:

1
sudo -u postgres psql -c "SHOW port;"

Expected:

1
5432

PostgreSQL is listening on:

1
127.0.0.1:5432

3. Create the SSH tunnel

From WSL2:

1
ssh -L 15432:127.0.0.1:5432 admin@192.168.1.65

Keep this terminal open.

The syntax is:

1
-L LOCAL_PORT:REMOTE_HOST:REMOTE_PORT

So:

1
2
3
4
5
15432:127.0.0.1:5432
  │        │          │
  │        │          └── PostgreSQL on Ubuntu
  │        └───────────── Ubuntu's localhost
  └────────────────────── WSL2 local port

4. Test the tunnel with psql

Open a second WSL2 terminal:

1
psql -h 127.0.0.1 -p 15432 -U postgres -d tompkins

Then:

1
SELECT version();

You successfully tested this with:

1
PostgreSQL 18.6

Exit:

1
\q

5. Set up Python

In WSL2:

1
2
3
4
5
6
7
mkdir -p ~/python-postgres
cd ~/python-postgres

python3 -m venv .venv
source .venv/bin/activate

pip install psycopg2-binary

6. Python connection

test_connection.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import psycopg2

conn = psycopg2.connect(
    host="127.0.0.1",
    port=15432,
    dbname="tompkins",
    user="postgres",
    password="YOUR_PASSWORD"
)

print("Connected to PostgreSQL!")

cur = conn.cursor()

cur.execute("SELECT version();")

result = cur.fetchone()

print(result[0])

cur.close()
conn.close()

Run:

1
python3 test_connection.py

You successfully obtained:

1
2
Connected to PostgreSQL!
PostgreSQL 18.6 ...

7. What is happening?

Python connects to:

1
127.0.0.1:15432

SSH forwards that connection to:

1
Ubuntu 127.0.0.1:5432

PostgreSQL receives it normally.

So Python does not directly connect to:

1
192.168.1.65:5432

and PostgreSQL does not need to be exposed to the network.


8. Do you need to create the tunnel every time?

With the manual approach, yes.

Every time you need the connection:

1
ssh -L 15432:127.0.0.1:5432 admin@192.168.1.65

Then run Python in another terminal.

You can later simplify this with SSH configuration or background tunnels.

Current workflow

1
2
3
4
5
6
7
8
Terminal 1 — WSL2
ssh -L 15432:127.0.0.1:5432 admin@192.168.1.65


Terminal 2 — WSL2
cd ~/python-postgres
source .venv/bin/activate
python3 test_connection.py

This is the fundamental Python → SSH tunnel → remote PostgreSQL pattern.

This post is licensed under CC BY 4.0 by the author.