Typing ssh -i ~/.ssh/prod_key -p 2222 -J bastion.example.com [email protected] gets old fast. SSH config files turn that mess into ssh prod-web. This guide shows you how to automate connections, manage keys, forward ports, and secure your remote access.
:::note[TL;DR]
- Create
~/.ssh/configfor host aliases and connection defaults - Use
Host *for global settings,Host aliasfor specific servers ProxyJump(or-J) for bastion/jump host connectionsLocalForwardfor port forwarding (local → remote)ControlMasterfor connection reuse (much faster)- Use
ed25519keys:ssh-keygen -t ed25519 -C "[email protected]":::
The SSH Config File
Your SSH config lives at ~/.ssh/config. Create it if it doesn’t exist:
touch ~/.ssh/config
chmod 600 ~/.ssh/config # Required permissions
Basic Structure
# Global settings (apply to all hosts)
Host *
User defaultuser
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
# Specific host alias
Host myserver
HostName 192.168.1.100
User ubuntu
Port 22
IdentityFile ~/.ssh/myserver_key
Now instead of:
ssh -i ~/.ssh/myserver_key [email protected]
Just type:
ssh myserver
Essential Config Options
Connection Basics
| Option | Description | Example |
|---|---|---|
HostName | Actual server address | server.example.com or 192.168.1.1 |
User | Login username | ubuntu, root, deploy |
Port | SSH port (default 22) | 2222, 443 |
IdentityFile | Private key path | ~/.ssh/id_ed25519 |
IdentitiesOnly | Use only specified key | yes (prevents SSH agent offering wrong keys) |
Connection Persistence
Host *
# Keep connections alive
ServerAliveInterval 60
ServerAliveCountMax 3
# Reuse connections (much faster)
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 4h
Create the socket directory:
mkdir -p ~/.ssh/sockets
Security Hardening
Host *
# Only use strong algorithms
HostKeyAlgorithms [email protected],ssh-ed25519
# Disable password auth (keys only)
PasswordAuthentication no
# Verify host keys strictly
StrictHostKeyChecking accept-new
# Hash known_hosts for privacy
HashKnownHosts yes
Real-World Examples
Development Server
Host dev
HostName dev.example.com
User developer
Port 22
IdentityFile ~/.ssh/dev_key
LocalForward 3000 localhost:3000 # Forward dev server
LocalForward 8080 localhost:8080 # Forward API
Usage:
ssh dev
# Access http://localhost:3000 on your machine
# Connects to dev server at localhost:3000
Production Server (Restricted)
Host prod
HostName prod.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/prod_key
IdentitiesOnly yes
ServerAliveInterval 30
# Restrict what can be done
PermitLocalCommand no
Multiple Servers with Patterns
# Match all staging servers
Host staging-*
HostName %h.staging.internal
User deploy
IdentityFile ~/.ssh/staging_key
# Match all production servers
Host prod-*
HostName %h.prod.internal
User deploy
IdentityFile ~/.ssh/prod_key
IdentitiesOnly yes
Usage:
ssh staging-web1 # Connects to staging-web1.staging.internal
ssh prod-api2 # Connects to prod-api2.prod.internal
Jump Hosts (Bastion)
Method 1: ProxyJump (Recommended)
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/bastion_key
Host internal-*
HostName %h.internal.corp
User ubuntu
IdentityFile ~/.ssh/internal_key
ProxyJump bastion
# Or: ProxyJump [email protected]
Usage:
ssh internal-webserver # Automatically hops through bastion
Method 2: ProxyCommand (Legacy)
Host internal-server
HostName 10.0.0.5
User ubuntu
IdentityFile ~/.ssh/internal_key
ProxyCommand ssh bastion -W %h:%p
Multiple Jump Hosts
Host secure-internal
HostName secure-server.internal
User admin
ProxyJump bastion1,bastion2 # Chain through both
Port Forwarding
Local Forward (Local → Remote)
Access remote services as if they’re local:
Host db-server
HostName db.internal.com
User admin
LocalForward 5433 localhost:5432 # Local 5433 → Remote 5432
Usage:
ssh db-server
# In another terminal:
psql -h localhost -p 5433 -U postgres # Connects to remote DB
Remote Forward (Remote → Local)
Let remote server access your local services:
Host dev-server
HostName dev.remote.com
User developer
RemoteForward 8080 localhost:3000 # Remote 8080 → Your local 3000
Usage:
ssh dev-server
# Colleagues can access your local dev server at dev.remote.com:8080
Dynamic Forward (SOCKS Proxy)
Host proxy
HostName jump-server.com
User admin
DynamicForward 1080 # Creates SOCKS proxy on port 1080
Usage:
ssh proxy
# Configure browser to use SOCKS5://localhost:1080
# All traffic routes through jump-server
SSH Key Management
Generating Keys
# Modern, secure, compact
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519
# For legacy systems (RSA)
ssh-keygen -t rsa -b 4096 -C "[email protected]" -f ~/.ssh/id_rsa
# For specific services
ssh-keygen -t ed25519 -C "github" -f ~/.ssh/github_key
ssh-keygen -t ed25519 -C "work-server" -f ~/.ssh/work_key
SSH Agent
# Start agent
eval "$(ssh-agent -s)"
# Add keys
ssh-add ~/.ssh/id_ed25519
ssh-add ~/.ssh/work_key
# List loaded keys
ssh-add -l
# Remove specific key
ssh-add -d ~/.ssh/work_key
# Clear all keys
ssh-add -D
Config with Multiple Keys
# GitHub
Host github.com
User git
IdentityFile ~/.ssh/github_key
IdentitiesOnly yes
# GitLab
Host gitlab.com
User git
IdentityFile ~/.ssh/gitlab_key
IdentitiesOnly yes
# Work servers
Host *.work.com
User developer
IdentityFile ~/.ssh/work_key
IdentitiesOnly yes
Advanced Configurations
LocalCommand (Run on Connect)
Host server-with-notification
HostName server.example.com
User admin
LocalCommand notify-send "Connected to %n" 2>/dev/null
PermitLocalCommand yes
Include External Configs
# Main config
Host *
ServerAliveInterval 60
# Include work-specific config
Include ~/.ssh/config.work
# Include personal config
Include ~/.ssh/config.personal
Match Directives
# Different settings for specific networks
Match host *.internal.corp exec "is_vpn_connected"
User admin
IdentityFile ~/.ssh/corp_key
Match host * !exec "is_vpn_connected"
# Don't allow corp connections without VPN
HostName invalid
Environment Variables
Host dynamic-server
HostName %h.dynamic-dns.com
User %u # Uses local username
Complete Example Config
# ~/.ssh/config
# ==========================================
# Global Settings
# ==========================================
Host *
# Connection
ServerAliveInterval 60
ServerAliveCountMax 3
# Performance
Compression yes
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 4h
# Security
HashKnownHosts yes
StrictHostKeyChecking accept-new
# Default key
IdentityFile ~/.ssh/id_ed25519
# ==========================================
# Personal Servers
# ==========================================
Host home-server
HostName 192.168.1.50
User ubuntu
LocalForward 8080 localhost:8080
LocalForward 3000 localhost:3000
Host pi
HostName raspberrypi.local
User pi
IdentityFile ~/.ssh/pi_key
# ==========================================
# Work Infrastructure
# ==========================================
Host work-bastion
HostName bastion.work.com
User admin
IdentityFile ~/.ssh/work_bastion_key
Host work-*
HostName %h.internal.work.com
User deploy
IdentityFile ~/.ssh/work_key
IdentitiesOnly yes
ProxyJump work-bastion
Host work-db
HostName database.internal.work.com
User dbadmin
LocalForward 5433 localhost:5432
ProxyJump work-bastion
Host work-cache
HostName redis.internal.work.com
User admin
LocalForward 6380 localhost:6379
ProxyJump work-bastion
# ==========================================
# Cloud Providers
# ==========================================
Host aws-*
User ec2-user
IdentityFile ~/.ssh/aws_key
StrictHostKeyChecking accept-new
Host gcp-*
User %u
IdentityFile ~/.ssh/gcp_key
Host do-*
User root
IdentityFile ~/.ssh/do_key
HostName %h.droplet.hostname
# ==========================================
# Git Services
# ==========================================
Host github.com
User git
IdentityFile ~/.ssh/github_personal
IdentitiesOnly yes
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/github_work
IdentitiesOnly yes
Host gitlab.com
User git
IdentityFile ~/.ssh/gitlab_key
IdentitiesOnly yes
Useful SSH Commands
# Test config validity
ssh -t [email protected]
# Verbose connection (debugging)
ssh -vvv myserver
# Connect with specific config
ssh -F ~/.ssh/config.work myserver
# Force specific key
ssh -i ~/.ssh/custom_key myserver
# Disable strict host checking (temporary, not recommended)
ssh -o StrictHostKeyChecking=no myserver
# Run command without interactive shell
ssh myserver "ls -la /var/log"
# Copy file via SCP with alias
scp myfile myserver:/tmp/
# Copy via jump host
scp -J bastion myfile internal-server:/tmp/
# Mount remote filesystem (SSHFS)
sshfs myserver:/remote/path /local/mount
# Sync with rsync over SSH
rsync -avz -e ssh ./local/ myserver:/remote/
Troubleshooting
Permission Issues
# Fix permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
# Fix SELinux (if enabled)
restorecon -Rv ~/.ssh
Too Many Authentication Failures
# Server rejects after too many key attempts
ssh -o IdentitiesOnly=yes myserver
Connection Timeout
Host slow-server
HostName slow.example.com
ConnectTimeout 30
ServerAliveInterval 30
Debug Connection Issues
# See exactly what's happening
ssh -vvv myserver 2>&1 | head -50
# Check which config is used
ssh -G myserver # Shows parsed config
Security Best Practices
- Use Ed25519 keys — Modern, secure, compact
- Disable password auth — Keys only in production
- Use separate keys per service — Compartmentalize
- Enable strict host checking — Prevent MITM attacks
- Hash known_hosts — Hide which servers you connect to
- Use ProxyJump — Don’t manually hop through bastions
- ControlMaster carefully — Understand connection reuse implications
- Review permissions — 600 for private keys, 700 for .ssh
Summary
- Config file:
~/.ssh/configturns long commands into short aliases - Host patterns: Use wildcards for server groups (
prod-*,*.internal) - ProxyJump: Automatic bastion hopping
- Port forwarding: Access remote services locally (
LocalForward) - Connection reuse:
ControlMasterfor speed - Multiple keys: Separate keys per service with
IdentitiesOnly
A good SSH config saves hours of typing and prevents connection errors. Set it up once, benefit forever.
What to Read Next
- SSH & GPG Cheat Sheet — Complete SSH and key management
- Linux & Bash Cheat Sheet — Terminal commands
- Setting Up SSH Keys for GitHub — Step-by-step guide