SSH Tips Every Developer Should Know

TL;DR

SSH config files eliminate repetitive flags. Jump hosts chain connections through bastions. Port forwarding tunnels any TCP traffic. Use Ed25519 keys. Set ControlMaster for connection reuse.

I used SSH for three years before I discovered ~/.ssh/config. Typing ssh -i ~/.ssh/mykey -p 2222 ubuntu@long-hostname.example.com every time was just the cost of doing business, I thought.

Then I spent 20 minutes on the config file and never typed a flag again.

The SSH Config File

Everything below goes in ~/.ssh/config. No restart needed — SSH reads it every connection.

# ~/.ssh/config

Host prod
    HostName 203.0.113.42
    User ubuntu
    IdentityFile ~/.ssh/prod_key
    Port 22

Host staging
    HostName 203.0.113.55
    User deploy
    IdentityFile ~/.ssh/staging_key
    Port 2222

# Wildcard for all company servers
Host *.internal.example.com
    User ec2-user
    IdentityFile ~/.ssh/company_key
    StrictHostKeyChecking no

Now ssh prod works. That's it.

Connection Reuse (ControlMaster)

Every SSH connection does a full handshake. If you're running scp, git push, and ssh in a loop, you're paying that cost repeatedly.

# ~/.ssh/config

Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets

First connection opens a socket. Subsequent connections to the same host reuse it — near-instant. ControlPersist 600 keeps the socket alive 10 minutes after you close the last session.

Before/after:

Without ControlMaster: git push → new SSH handshake every time (~300ms)
With ControlMaster:    git push → reuses socket (~20ms)

This matters when git push is over SSH and you're doing it 50 times a day.

Jump Hosts (Bastion Servers)

Your production servers aren't publicly reachable. You SSH to a bastion first, then to the target. Old way:

ssh bastion
# now on bastion
ssh internal-server

Better way with ProxyJump:

Host bastion
    HostName bastion.example.com
    User ec2-user
    IdentityFile ~/.ssh/company_key

Host internal-server
    HostName 10.0.1.50
    User ubuntu
    ProxyJump bastion

Now ssh internal-server automatically goes through the bastion. No manual two-step.

Chain multiple jump hosts if needed:

Host deep-internal
    HostName 10.0.2.100
    ProxyJump bastion,internal-server

From the command line:

ssh -J bastion ubuntu@10.0.1.50

Port Forwarding

Local Forwarding (access remote service locally)

# Forward local port 5432 to the database behind a bastion
ssh -L 5432:db.internal:5432 bastion

# Now connect your local Postgres client to localhost:5432
psql -h localhost -p 5432 -U postgres mydb

In config:

Host db-tunnel
    HostName bastion.example.com
    User ec2-user
    LocalForward 5432 db.internal:5432
    LocalForward 6379 redis.internal:6379
    # Forward multiple services at once
ssh -N db-tunnel   # -N: don't open a shell, just keep tunnels open

Remote Forwarding (expose local service to remote server)

# Expose your local port 3000 on the remote server's port 8080
ssh -R 8080:localhost:3000 server

# Someone can now hit server:8080 and reach your local dev environment

Useful for demos, webhooks in development, testing on a remote device.

Dynamic Forwarding (SOCKS proxy)

ssh -D 9090 bastion

# Configure your browser to use SOCKS5 proxy at localhost:9090
# All browser traffic routes through the bastion

Instant VPN-like access to everything the bastion can reach.

Key Management

# Generate Ed25519 key (modern, fast, small)
ssh-keygen -t ed25519 -C "your@email.com"

# Old RSA is fine, but use 4096 bits minimum if you must
ssh-keygen -t rsa -b 4096 -C "your@email.com"

# Add key to agent (avoid entering passphrase repeatedly)
ssh-add ~/.ssh/id_ed25519

# Add to macOS keychain permanently
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# ~/.ssh/config — auto-add keys to agent on macOS
Host *
    AddKeysToAgent yes
    UseKeychain yes           # macOS only
    IdentityFile ~/.ssh/id_ed25519

Copy public key to server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

# Or manually:
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Useful One-Liners

# Run a command on remote server without opening a shell
ssh server "df -h && uptime"

# Copy files (scp uses SSH)
scp file.txt server:/tmp/
scp -r ./dir server:/opt/

# Mount remote filesystem locally (macFUSE/sshfs on Linux)
sshfs server:/var/www /mnt/remote

# Check which key will be used for a host
ssh -v server 2>&1 | grep "Offering public key"

# Escape hatch when SSH hangs: ~. (tilde dot)
# Type it to terminate a hung connection

# Get local SSH agent socket into a sudo shell
ssh -A server   # Forward agent, then sudo -i will still have agent access

Hardening (For Servers You Run)

# /etc/ssh/sshd_config

PasswordAuthentication no          # Keys only
PermitRootLogin no                 # Never root direct
MaxAuthTries 3                     # Limit brute force
ClientAliveInterval 300            # Disconnect idle sessions
ClientAliveCountMax 2
AllowUsers ubuntu deploy           # Explicit allowlist
sudo systemctl reload sshd

Debugging Connections

# Verbose output — shows exactly what's happening
ssh -v server       # 1x verbose
ssh -vv server      # 2x verbose
ssh -vvv server     # 3x verbose (full debug)

# Common issues:
# "Permission denied" → wrong key, wrong user, or key not in authorized_keys
# Connection hangs → firewall blocking port, try -v to see where it stops
# "Host key verification failed" → server fingerprint changed, edit ~/.ssh/known_hosts

The Bottom Line

SSH is a power tool that most people use as a basic drill. The config file alone eliminates 80% of repetitive typing.

The rules:

  • Put everything in ~/.ssh/config — stop typing flags
  • Enable ControlMaster for connection reuse, especially on slow networks
  • Use ProxyJump instead of two-step SSH to bastions
  • Port forwarding lets you securely tunnel any TCP service
  • Use Ed25519 keys — smaller, faster, more modern than RSA
  • Disable password auth on any server you manage

SSH is one of those tools where 2 hours of learning saves 2 hours per week forever.