How to SSH Tunnel to a Remote PostgreSQL Container

8 min read.
Tags: devops

If your PostgreSQL lives inside a Docker container on a remote server — not exposed to the internet — SSH tunneling is the cleanest way to reach it from your laptop. No firewall rules, no open ports, no VPN. Just SSH.

This guide walks through the exact steps with commands you can copy and adapt.

Why bother with an SSH tunnel?

Opening port 5432 to the public internet is a bad idea. With an SSH tunnel, the connection stays private:

  • Your database port never touches the public internet
  • Access is gated by your existing SSH credentials (or better, SSH keys)
  • Local tools like psql, DBeaver, TablePlus, and Prisma Studio work exactly as if the database were on your own machine

The traffic path looks like this:

local machine → SSH server → PostgreSQL container

What you'll need

Before starting, make sure you have:

  • SSH access to the remote host
  • Docker running on that host with a PostgreSQL container
  • The container name (or ID)
  • The PostgreSQL port inside the container (usually 5432)
  • A free local port to bind to (this guide uses 5433)

SSH key authentication is strongly recommended over password login — it's more secure and saves you from typing a password every session.

Step 1: Find the container's internal IP

Docker's bridge network gives each container its own IP that the host machine can reach. Grab it with:

ssh root@<remote_host> "docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container_name>"

You should get back something like 172.18.0.5.

If nothing comes back, double-check the container name first:

ssh root@<remote_host> "docker ps --format '{{.Names}}'"

Step 2: Open the tunnel

ssh -N -L <local_port>:<container_ip>:<container_port> root@<remote_host>

A concrete example:

ssh -N -L 5433:172.18.0.5:5432 root@example.com

The -N flag tells SSH not to run a remote command — just forward the port. Keep this terminal open for as long as you need the connection.

Step 3: Connect with your local tools

With the tunnel running, connect to 127.0.0.1 on your chosen local port using the same credentials as the remote database:

  • Host: 127.0.0.1
  • Port: 5433 (or whatever local port you picked)
  • User / password / database: same as on the remote PostgreSQL instance

With psql:

psql -h 127.0.0.1 -p 5433 -U <db_user> -d <db_name>

Any GUI client (DBeaver, TablePlus, DataGrip) works the same way — just point it at localhost and your tunnel port.

Running the tunnel in the background

If you need the tunnel open without occupying a terminal, add -f:

ssh -f -N -L 5433:172.18.0.5:5432 root@example.com

To close it later:

ps aux | grep "ssh -f -N -L 5433"
kill <pid>

For something more permanent — like a dev server you tunnel into every day — look at autossh, which keeps the tunnel alive and reconnects automatically if it drops.

A few security pointers

  • Don't expose port 5432 publicly. The whole point of this setup is that you don't have to.
  • Use SSH keys. They're harder to brute-force than passwords and easier to manage once set up.
  • Avoid root when you can. If your server allows a limited-privilege user with SSH access, prefer that over root for day-to-day tunneling.

Common questions

Can I use this with Prisma?

Yes. Set DATABASE_URL to postgresql://<user>:<password>@127.0.0.1:<local_port>/<db_name> while the tunnel is active. Prisma doesn't know or care that it's going through a tunnel.

What if PostgreSQL is on the host network, not inside a container?

Skip the container IP lookup and use 127.0.0.1 as the target in the tunnel command:

ssh -N -L 5433:127.0.0.1:5432 root@example.com

Do I need autossh?

Not for occasional use. For tunnels you want always running without babysitting, autossh is worth the five-minute setup.

Related Posts