All articles🇬🇧 English
10 min

SSH Port Forwarding: Secure Access to Server Applications

How to securely access admin panels, pgAdmin, and other applications on your server without exposing them to the internet. Complete guide to SSH tunneling with practical examples.

sshsecurityport-forwardingtunnelingserver

The Problem

You have a production server with several applications:

  • Admin panel on port 3001
  • pgAdmin for PostgreSQL management on port 5050
  • Grafana for monitoring on port 3000
  • RabbitMQ Management on port 15672

You want to access them for administration, but you don't want to expose these ports to the internet. And that's correct! Publishing admin panels is a huge security hole.

How do you solve this? SSH Port Forwarding (SSH tunneling) is your best friend.

What is SSH Port Forwarding?

SSH Port Forwarding (or SSH tunneling) is a technique that allows you to securely redirect network traffic through an encrypted SSH connection.

Simple Analogy

Imagine you have a secret tunnel between your home and the bank. Instead of walking down the street (open internet) where everyone can see you carrying money, you walk through a closed tunnel. Only you and the bank know it exists.

SSH tunnel is the same principle for network traffic:

  • 🔒 All traffic is encrypted
  • 🚫 Ports are not exposed to the internet
  • 🔑 Requires SSH key for access
  • 👤 Access only for authorized users

Why Use SSH Port Forwarding?

✅ Benefits

  1. Security First

    • No public exposure of admin panels
    • All traffic encrypted via SSH protocol
    • No need to configure additional authentication
    • Protection from DDoS attacks on admin interfaces
  2. Easy Setup

    • No need to configure VPN
    • Works out of the box (SSH already configured)
    • One command for access
    • No firewall changes required
  3. Flexibility

    • Can forward any number of ports
    • Works with any protocols (HTTP, PostgreSQL, Redis, etc.)
    • Can forward ports from multiple servers simultaneously
    • Easy to automate
  4. Performance

    • Minimal overhead
    • Native SSH support
    • No additional software required

⚠️ Alternatives and When to Use Them

SolutionWhen to UseWhen NOT to Use
SSH Port ForwardingPersonal developer/admin accessTeam of many people needs access
VPN (WireGuard/OpenVPN)Permanent access for entire teamOne-time access for 1-2 people
Bastion HostLarge infrastructure with dozens of serversSingle server
Cloudflare TunnelWeb access needed without VPN for teamOnly CLI/Database tools

Types of SSH Port Forwarding

SSH supports three types of port forwarding:

1. Local Port Forwarding (-L) — Most Common Case

What it does: Forwards a port from the remote server to your local computer.

Syntax:

ssh -L [local_port]:[destination_host]:[destination_port] user@ssh_server

Diagram:

Your Computer → SSH Server → Destination
(localhost:8080) → (ssh tunnel) → (server:3001)

2. Remote Port Forwarding (-R) — For Demos and Callbacks

What it does: Forwards a port from your local computer to the remote server.

Syntax:

ssh -R [remote_port]:[destination_host]:[destination_port] user@ssh_server

3. Dynamic Port Forwarding (-D) — SOCKS Proxy

What it does: Creates a SOCKS proxy to route all traffic through SSH.

Syntax:

ssh -D [local_port] user@ssh_server

Practical Examples

Example 1: Accessing Admin Panel

You have an admin panel at http://localhost:3001 on the server. You want to open it in your browser on your computer.

Command:

ssh -L 8080:localhost:3001 user@your-server.com

What happens:

  1. SSH creates a tunnel to the server
  2. Your local port 8080 is linked to port 3001 on the server
  3. You open browser: http://localhost:8080
  4. All traffic goes through encrypted SSH tunnel

After running the command:

# In another terminal or just in browser
curl http://localhost:8080
# or open in browser: http://localhost:8080

Example 2: Accessing pgAdmin for PostgreSQL

PostgreSQL database on server, pgAdmin running in Docker on port 5050.

Command:

ssh -L 5050:localhost:5050 user@your-server.com

Usage:

# Open in browser
http://localhost:5050

# Log into pgAdmin
# Create connection to PostgreSQL via localhost

Example 3: Direct PostgreSQL Connection

Want to connect to PostgreSQL directly via DBeaver, DataGrip, or any other client?

Command:

ssh -L 5432:localhost:5432 user@your-server.com

Settings in DBeaver/DataGrip:

Host: localhost
Port: 5432
Database: your_database
User: your_user
Password: your_password

Important: You're now connecting to local port 5432, but traffic goes to the server through the tunnel!

Example 4: Multiple Ports Simultaneously

Need access to admin panel, pgAdmin, and Grafana?

Command:

ssh -L 8080:localhost:3001 \
    -L 5050:localhost:5050 \
    -L 3000:localhost:3000 \
    user@your-server.com

Now available:

  • Admin panel: http://localhost:8080
  • pgAdmin: http://localhost:5050
  • Grafana: http://localhost:3000

Example 5: Accessing Application on Another Server

You have a Jump Server (bastion host), and the application is on another server in a private network.

Diagram:

Your PC → Jump Server → Private Server (10.0.0.5:3001)

Command:

ssh -L 8080:10.0.0.5:3001 user@jump-server.com

What happens:

  1. SSH tunnel is created to jump-server.com
  2. Through this tunnel, traffic goes to 10.0.0.5:3001
  3. You work with http://localhost:8080

Example 6: Background Mode

Don't want to keep terminal open? Run SSH in background.

Command:

ssh -f -N -L 8080:localhost:3001 user@your-server.com

Options:

  • -f — run in background
  • -N — don't execute commands (tunnel only)
  • -L — local port forwarding

Termination:

# Find process
ps aux | grep "ssh -f"

# Kill process
kill <PID>

# Or more elegantly
pkill -f "ssh -f.*8080:localhost:3001"

Example 7: Auto-Reconnect

SSH tunnel can break. Use autossh for automatic reconnection.

Installation (Ubuntu/Debian):

sudo apt install autossh

Command:

autossh -M 0 -f -N -L 8080:localhost:3001 user@your-server.com

Options:

  • -M 0 — disable monitoring port (use ServerAlive)

Configuration in ~/.ssh/config:

Host your-server
    ServerAliveInterval 30
    ServerAliveCountMax 3

SSH Config for Convenience

Create ~/.ssh/config to simplify commands:

Host myserver
    HostName your-server.com
    User your-username
    Port 22
    IdentityFile ~/.ssh/id_rsa
    LocalForward 8080 localhost:3001
    LocalForward 5050 localhost:5050
    LocalForward 3000 localhost:3000
    ServerAliveInterval 60
    ServerAliveCountMax 3

Now the command is simplified:

# Was
ssh -L 8080:localhost:3001 -L 5050:localhost:5050 -L 3000:localhost:3000 user@your-server.com

# Became
ssh myserver

Security and Best Practices

✅ What to Do

  1. Use SSH Keys Instead of Passwords
# Generate SSH key
ssh-keygen -t ed25519 -C "your_email@example.com"

# Copy to server
ssh-copy-id user@your-server.com
  1. Disable Root Login and Passwords on Server
# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
  1. Configure fail2ban
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
  1. Use Non-Standard SSH Port
# /etc/ssh/sshd_config
Port 2222  # instead of 22
  1. Restrict Access by IP (if possible)
# UFW
sudo ufw allow from YOUR_IP to any port 2222

# or in /etc/ssh/sshd_config
AllowUsers user@YOUR_IP
  1. Use SSH Agent Forwarding Carefully
# Only for trusted servers
ssh -A user@trusted-server.com

❌ What NOT to Do

  1. Don't Forward Ports to 0.0.0.0
# BAD: accessible to everyone on local network
ssh -L 0.0.0.0:8080:localhost:3001 user@server.com

# GOOD: accessible only locally
ssh -L 127.0.0.1:8080:localhost:3001 user@server.com
# or simply
ssh -L 8080:localhost:3001 user@server.com
  1. Don't Use Weak SSH Passwords
  2. Don't Leave Tunnels Open When Not Needed
  3. Don't Log SSH Passwords in Scripts

Troubleshooting

Problem: "Permission denied (publickey)"

Solution:

# Check SSH key permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub

# Add key to SSH agent
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa

# Check that key is on server
cat ~/.ssh/authorized_keys

Problem: "Address already in use"

Solution:

# Find process using the port
sudo lsof -i :8080

# Kill process
kill <PID>

# Or use different local port
ssh -L 8081:localhost:3001 user@server.com

Problem: "Connection refused"

Possible causes:

  1. Application on server is not running
  2. Application listens only on specific IP (not 0.0.0.0 or localhost)
  3. Firewall blocks connection

Diagnostics on server:

# Check that application is running
sudo netstat -tlnp | grep :3001

# or
sudo ss -tlnp | grep :3001

# Check which interface the application is listening on
# If 0.0.0.0:3001 or 127.0.0.1:3001 — ok
# If 192.168.1.5:3001 — need to forward through this IP

ssh -L 8080:192.168.1.5:3001 user@server.com

Problem: Tunnel Disconnects

Solution:

# Add to ~/.ssh/config
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    TCPKeepAlive yes

# Or use autossh
autossh -M 0 -f -N -L 8080:localhost:3001 user@server.com

Real-World Usage Scenario

Here's how I use SSH Port Forwarding in production:

Scenario: Managing Microservices Architecture

Infrastructure:

  • Jump Server (bastion host) with public IP
  • 3 private servers with microservices
  • PostgreSQL on separate server
  • Redis cluster
  • RabbitMQ

My ~/.ssh/config:

Host bastion
    HostName 203.0.113.10
    User admin
    Port 2222
    IdentityFile ~/.ssh/production_key
    ServerAliveInterval 60

Host db-admin
    HostName 203.0.113.10
    User admin
    Port 2222
    IdentityFile ~/.ssh/production_key
    LocalForward 5432 10.0.1.10:5432
    LocalForward 5050 10.0.1.10:5050
    ServerAliveInterval 60

Host monitoring
    HostName 203.0.113.10
    User admin
    Port 2222
    IdentityFile ~/.ssh/production_key
    LocalForward 3000 10.0.2.15:3000
    LocalForward 9090 10.0.2.15:9090
    ServerAliveInterval 60

Host admin-panel
    HostName 203.0.113.10
    User admin
    Port 2222
    IdentityFile ~/.ssh/production_key
    LocalForward 8080 10.0.3.20:3001
    ServerAliveInterval 60

Usage:

# Database access
ssh db-admin
# Now available:
# - PostgreSQL: localhost:5432
# - pgAdmin: http://localhost:5050

# Monitoring
ssh monitoring
# Now available:
# - Grafana: http://localhost:3000
# - Prometheus: http://localhost:9090

# Admin panel
ssh admin-panel
# http://localhost:8080

Automation via Script

~/scripts/connect-dev.sh:

#!/bin/bash

echo "🔌 Connecting to dev environment..."

# Start tunnels in background
ssh -f -N db-admin
ssh -f -N monitoring
ssh -f -N admin-panel

echo "✅ Tunnels established!"
echo ""
echo "📊 Available services:"
echo "  - pgAdmin: http://localhost:5050"
echo "  - PostgreSQL: localhost:5432"
echo "  - Grafana: http://localhost:3000"
echo "  - Admin Panel: http://localhost:8080"
echo ""
echo "To disconnect: ~/scripts/disconnect-dev.sh"

~/scripts/disconnect-dev.sh:

#!/bin/bash

echo "🔌 Disconnecting from dev environment..."

pkill -f "ssh -f -N db-admin"
pkill -f "ssh -f -N monitoring"
pkill -f "ssh -f -N admin-panel"

echo "✅ Disconnected!"

Conclusion

SSH Port Forwarding is a powerful and secure way to access internal services on your servers without exposing them to the internet.

Key Takeaways

  1. 🔒 Security: No public exposure, all traffic encrypted
  2. 🚀 Simplicity: One command for access, no VPN needed
  3. 🎯 Flexibility: Works with any protocols and services
  4. Performance: Minimal overhead

When to Use SSH Port Forwarding

Use for:

  • Personal access to admin panels
  • Database connections for development
  • Access to monitoring tools (Grafana, Prometheus)
  • Temporary access to internal APIs
  • Debugging production issues

Don't use for:

  • Permanent access for large teams (better use VPN)
  • Production traffic from users
  • Latency-critical applications
  • When web access is needed without SSH client

Next Steps

  1. Configure SSH keys instead of passwords
  2. Create ~/.ssh/config with your servers
  3. Install autossh for reliability
  4. Write scripts for quick connection
  5. Regularly rotate SSH keys

Remember: Security starts with the right tools. SSH Port Forwarding is one of them.


Have questions? Found an error? Write me on Telegram: [@your_telegram]

SSH Port Forwarding: Secure Access to Server Applications | ByteGuide