Quick Answer:
ssh user@serverconnects to a server.ssh-keygen -t ed25519generates a key.ssh-copy-id user@serversets up passwordless login.ssh -L 8080:localhost:80 user@servercreates a local tunnel.ssh -D 1080 user@servercreates a SOCKS proxy.
SSH (Secure Shell) is the standard way to remotely manage Linux servers. It encrypts all traffic between your machine and the server — commands, file transfers, and even forwarded ports are all secured.
This guide covers everything from basic connections to advanced tunneling and security.
Part 1: Basic Connection
# Connect to a server
ssh [email protected]
ssh [email protected]
# Connect on a different port
ssh -p 2222 user@server
# Run a single command (doesn't open shell)
ssh user@server "uptime"
ssh user@server "df -h && free -m"
Part 2: SSH Keys
Passwords are slow and insecure. SSH keys are faster, stronger, and enable automation.
Generate a Key
# Ed25519 (recommended — modern, fast, secure)
ssh-keygen -t ed25519 -C "[email protected]"
# RSA (for older servers that don't support Ed25519)
ssh-keygen -t rsa -b 4096 -C "[email protected]"
This creates:
~/.ssh/id_ed25519— Private key (never share this)~/.ssh/id_ed25519.pub— Public key (copy to servers)
Copy Key to Server
# Automatic (easiest)
ssh-copy-id user@server
# Manual (if ssh-copy-id not available)
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
# From Windows PowerShell
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Test
ssh user@server
# Should connect without asking for password
Key Permissions
SSH refuses to use keys with loose permissions:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519 # Private key
chmod 644 ~/.ssh/id_ed25519.pub # Public key
chmod 600 ~/.ssh/authorized_keys # On the server
chmod 600 ~/.ssh/config # Config file
Full guide: How to Set Up SSH Keys
Part 3: SSH Config File
The config file saves connection details so you don't type them every time.
Create or edit ~/.ssh/config:
# Simple alias
Host myserver
HostName 203.0.113.50
User sam
Port 2222
# Now just type: ssh myserver
# Different key per server
Host work
HostName work.example.com
User admin
IdentityFile ~/.ssh/id_work
Host personal
HostName 10.0.0.5
User sam
IdentityFile ~/.ssh/id_personal
# Wildcard defaults
Host *
ServerAliveInterval 30
ServerAliveCountMax 3
AddKeysToAgent yes
Useful Config Options
| Option | What It Does | Example |
|---|---|---|
HostName |
Server IP or domain | 203.0.113.50 |
User |
Login username | sam |
Port |
SSH port | 2222 |
IdentityFile |
Which key to use | ~/.ssh/id_work |
ServerAliveInterval |
Send keepalive every N seconds | 30 |
ServerAliveCountMax |
Disconnect after N missed keepalives | 3 |
ProxyJump |
Jump through another server | bastion |
LocalForward |
Auto-create tunnel on connect | 8080 localhost:80 |
DynamicForward |
Auto-create SOCKS proxy | 1080 |
ForwardAgent |
Forward SSH agent to server | yes |
Compression |
Compress data (slow connections) | yes |
Full guide: SSH Config Guide
Part 4: SSH Tunnels
SSH tunnels encrypt traffic between ports on your local machine and a remote server.
Local Port Forwarding (-L)
Access a remote service as if it were local.
# Forward local port 8080 to remote port 80
ssh -L 8080:localhost:80 user@server
# Now open http://localhost:8080 — it goes to server:80
Use case: Access a database or web panel on a server that only listens on localhost.
# Access remote PostgreSQL
ssh -L 5433:localhost:5432 user@dbserver
# Connect to localhost:5433 — goes to server's PostgreSQL
# Access remote web admin panel
ssh -L 9090:localhost:9090 user@server
# Open http://localhost:9090 in your browser
Remote Port Forwarding (-R)
Make your local machine accessible from the server.
# Expose local port 3000 on the server as port 8080
ssh -R 8080:localhost:3000 user@server
# Someone on the server can now access your app at localhost:8080
Use case: Show a local development site to someone on the server.
Dynamic Port Forwarding (-D) — SOCKS Proxy
Create a SOCKS5 proxy through the SSH connection.
ssh -D 1080 -N -f user@server
-D 1080— SOCKS proxy on localhost:1080-N— Don't execute commands (tunnel only)-f— Run in background
Configure your browser to use SOCKS5 proxy at 127.0.0.1:1080. All browsing goes through the server.
Full guide: SOCKS5 Proxy Setup
Keep Tunnels Alive
# autossh reconnects if the connection drops
sudo apt install autossh
autossh -M 0 -D 1080 -N -f user@server \
-o "ServerAliveInterval=30" \
-o "ServerAliveCountMax=3"
Or use tunnelforge for managed SSH tunnels with TUI, TLS obfuscation, and Telegram alerts.
Part 5: File Transfer
SCP (Simple Copy)
# Upload file
scp file.txt user@server:/remote/path/
# Download file
scp user@server:/remote/path/file.txt ./local/
# Upload directory
scp -r mydir/ user@server:/remote/path/
# With custom port
scp -P 2222 file.txt user@server:/path/
rsync (Better for Large Transfers)
# Sync directory (only copies changes)
rsync -avz local-dir/ user@server:/remote-dir/
# With progress
rsync -avz --progress local-dir/ user@server:/remote-dir/
# Delete files on remote that don't exist locally
rsync -avz --delete local-dir/ user@server:/remote-dir/
# With custom port
rsync -avz -e "ssh -p 2222" local-dir/ user@server:/remote-dir/
# Dry run (see what would change)
rsync -avzn local-dir/ user@server:/remote-dir/
SFTP (Interactive)
sftp user@server
# Commands inside SFTP:
ls # List remote files
lls # List local files
cd /path # Change remote directory
lcd /path # Change local directory
get file.txt # Download
put file.txt # Upload
get -r dir/ # Download directory
put -r dir/ # Upload directory
exit
Part 6: Jump Hosts (ProxyJump)
Access a server through an intermediate bastion/jump host.
Your machine → Bastion (public) → Internal Server (private)
# One command
ssh -J bastion-user@bastion internal-user@internal-server
# In SSH config (cleaner)
Host bastion
HostName 203.0.113.50
User admin
Host internal
HostName 10.0.0.5
User sam
ProxyJump bastion
# Now just: ssh internal
Multi-Hop
# Through two jump hosts
ssh -J host1,host2 final-server
# In config
Host final
HostName 10.0.0.100
ProxyJump host1,host2
Part 7: SSH Agent
The SSH agent holds your decrypted keys in memory so you don't type passphrases repeatedly.
# Start the agent
eval "$(ssh-agent -s)"
# Add your key
ssh-add ~/.ssh/id_ed25519
# List loaded keys
ssh-add -l
# Remove all keys from agent
ssh-add -D
Agent Forwarding
Use your local keys on a remote server (for git pull on the server without copying keys):
ssh -A user@server
# Or in config:
Host server
ForwardAgent yes
Security warning: Only use agent forwarding to servers you trust. A compromised server could use your forwarded agent to access other servers.
Part 8: Security Hardening
Essential Changes (/etc/ssh/sshd_config)
Port 2222 # Change from default 22
PermitRootLogin no # Never SSH as root
PasswordAuthentication no # Keys only
MaxAuthTries 3 # Limit login attempts
ClientAliveInterval 300 # Send keepalive every 5 min (disconnect after 10 min with CountMax 2)
ClientAliveCountMax 2 # After 2 missed keepalives
AllowUsers sam admin # Only these users can SSH
X11Forwarding no # Disable if not needed
sudo sshd -t # Test config
sudo systemctl restart sshd # Apply
Install Fail2ban
sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban
Automatically bans IPs after failed login attempts.
Full guides:
Part 9: Troubleshooting
Debug Connection
ssh -v user@server # Verbose
ssh -vv user@server # More verbose
ssh -vvv user@server # Maximum debug output
Common Problems
| Problem | Fix | |
|---|---|---|
| Permission denied (publickey) | Key not on server. Run ssh-copy-id user@server |
|
| Connection refused | SSH not running or wrong port. Check `ss -tlnp \ | grep ssh` on server |
| Connection timed out | Firewall blocking. Check ufw status or cloud security groups |
|
| Host key verification failed | Server was reinstalled. Remove old key: ssh-keygen -R server-ip |
|
| Key too open | chmod 600 ~/.ssh/id_ed25519 |
|
| Agent has no identities | ssh-add ~/.ssh/id_ed25519 |
|
| Broken pipe / disconnect | Add ServerAliveInterval 30 to SSH config |
Check What's Listening
# On the server
sudo ss -tlnp | grep ssh
# Should show sshd on port 22 (or custom port)
Part 10: Useful One-Liners
# Mount remote directory locally (SSHFS)
sudo apt install sshfs
sshfs user@server:/remote/path /local/mount
# Run local script on remote server
ssh user@server 'bash -s' < local-script.sh
# Copy SSH public key to clipboard (Mac)
pbcopy < ~/.ssh/id_ed25519.pub
# Copy SSH public key to clipboard (Linux)
xclip -sel clip < ~/.ssh/id_ed25519.pub
# Generate a key without prompts (for automation)
ssh-keygen -t ed25519 -f ~/.ssh/id_auto -N "" -q
# Test SSH connection without logging in
ssh -o ConnectTimeout=5 -o BatchMode=yes user@server echo ok
Related Guides on SamNet
SSH Setup:
Security:
Tunnels & Proxy:
Tools:
- tunnelforge — SSH tunnel manager with TUI and TLS obfuscation
- VPN Leak Test — check if your tunnel is leaking
- Port Scanner — test if SSH port is accessible