You have a server running a database. You are on your laptop and you want to connect to that database with a graphical tool to run some queries. The natural first thought is to make the database accept connections from the internet. So you open port 5432 on your server’s firewall and configure the database to listen on its public IP address.
This is a terrible idea. Exposing your database directly to the public internet is a huge security risk. Automated scanners are constantly looking for open ports and they will find yours. They will try to guess your passwords or exploit any known vulnerability. It is a matter of when not if you will have a problem.
You could set up a complex VPN. That is a valid solution but it is often more work than you need for a simple task. There is a much simpler way that uses a tool you already have and use every day. It is called SSH port forwarding or an SSH tunnel.
SSH port forwarding creates a secure tunnel from your local machine to the remote server. The connection inside the tunnel is encrypted by SSH. You tell SSH to listen on a port on your local machine. When you connect to that local port your traffic gets sent securely through the tunnel to the remote server. The remote server then opens a connection to the final destination for you.
It is like having a secure private pipe. You connect one end to your laptop and the other end to a service running on or near your remote server. It gives you local access to remote services without exposing those services to the outside world.
Most of the time you will use local port forwarding. The command looks a little complicated at first but it is simple once you break it down.
ssh -L local_port:destination_host:destination_port user@ssh_server
Let’s look at each part.
-L stands for Local. It tells SSH you want to forward a local port.
local_port is the port number you will connect to on your own machine. You can choose any high number port that is not already in use say 8000.
destination_host is the host the remote server should connect to. If your database is running on the same server you are SSHing into this will usually be
localhost
.
destination_port is the port the service is listening on at the destination. For PostgreSQL this is usually
5432
.
user@ssh_server is the regular SSH login for your remote server.
So you are telling SSH to listen on a local port and forward any connections to a destination host and port from the perspective of the remote server.
Let’s make this concrete. Imagine your production server is at the IP address 123.45.67.89
. Your PostgreSQL database is running on that server and it is configured for security so it only listens for connections from localhost
on port 5432
.
To connect to it from your laptop you open a terminal and run this command.
# Format ssh -L <your_local_port>:<db_host_on_server>:<db_port> <user>@<server_ip>
ssh -L 8000:localhost:5432 myuser@123.45.67.89
When you run this you will see a normal SSH login prompt. Enter your password or use your SSH key. You will be logged into your server. Just leave this terminal window open. The tunnel is active as long as your SSH session is active.
Now open your favorite database client on your laptop. Instead of connecting to the remote server’s IP address you connect to your own machine.
localhost
or 127.0.0.1
8000
(the local port you chose)Click connect. It will work. Your database client thinks it is talking to a database on your local machine at port 8000. But the SSH tunnel is transparently forwarding all that traffic to localhost:5432
on the remote server. It is secure simple and feels like magic.
Sometimes your network is more complex. You might have a bastion or jump host that you can SSH into from the internet. Your database might be on a different server in a private network that is only accessible from that jump host.
SSH port forwarding handles this easily. Let’s say your jump host is jump.myservice.com
. Your private database server has the IP 10.0.0.5
and is listening on port 5432
.
The command is almost the same. The only change is the destination_host
.
# Format ssh -L <your_local_port>:<private_db_ip>:<db_port> <user>@<jump_server>
ssh -L 8000:10.0.0.5:5432 myuser@jump.myservice.com
Here your connection goes through the SSH tunnel to the jump host. The jump host then makes a new connection to the database at 10.0.0.5
. You still connect your local database client to localhost:8000
just like before. This technique is a cornerstone of secure network design.
Keeping a terminal window open just for the tunnel can be annoying. You can add two flags to run the tunnel in the background.
ssh -f -N -L 8000:localhost:5432 myuser@123.45.67.89
-N tells SSH to not execute a remote command. It is useful for just forwarding ports.
-f tells SSH to go into the background just before it executes the command.
This is convenient but you have to remember to find and kill the process when you are done. A better long term solution is to use your SSH config file located at ~/.ssh/config
.
You can add an entry like this.
Host prod-db-tunnel
HostName 123.45.67.89
User myuser
LocalForward 8000 localhost:5432
Now all you have to do is run ssh prod-db-tunnel
and the tunnel will be established. When you close the session the tunnel closes too.
SSH port forwarding is not an obscure trick. It is a fundamental tool for working securely. It lets you interact with any remote service as if it were running on your machine all without poking holes in your firewall.
Think about the other services you could securely connect to this way.
Give it a try for yourself and let me know what’s one tool you've found that feels like magic?
— Rishi Banerjee
September 2025