M
MeshWorld.
SSH DevOps Security Terminal Remote Access Developer Tools Linux Server Management 8 min read

SSH Config & Aliases: The Developer's Connection Kit

Vishnu
By Vishnu

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/config for host aliases and connection defaults
  • Use Host * for global settings, Host alias for specific servers
  • ProxyJump (or -J) for bastion/jump host connections
  • LocalForward for port forwarding (local → remote)
  • ControlMaster for connection reuse (much faster)
  • Use ed25519 keys: 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

OptionDescriptionExample
HostNameActual server addressserver.example.com or 192.168.1.1
UserLogin usernameubuntu, root, deploy
PortSSH port (default 22)2222, 443
IdentityFilePrivate key path~/.ssh/id_ed25519
IdentitiesOnlyUse only specified keyyes (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)

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

  1. Use Ed25519 keys — Modern, secure, compact
  2. Disable password auth — Keys only in production
  3. Use separate keys per service — Compartmentalize
  4. Enable strict host checking — Prevent MITM attacks
  5. Hash known_hosts — Hide which servers you connect to
  6. Use ProxyJump — Don’t manually hop through bastions
  7. ControlMaster carefully — Understand connection reuse implications
  8. Review permissions — 600 for private keys, 700 for .ssh

Summary

  • Config file: ~/.ssh/config turns 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: ControlMaster for 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.