The SSH Config File

Chapter 4 — The SSH Config File

By now you know how to connect, use keys, and specify flags. But typing ssh -p 2222 -i ~/.ssh/id_work philip@work-server.example.com every time is nobody's idea of fun. The SSH config file — ~/.ssh/config — lets you store all of that per host, so the full command above becomes simply ssh work. It also handles jump hosts, keepalives, agent forwarding, and anything else you'd normally pass as a flag.

Location: ~/.ssh/config on Linux and macOS. On Windows with OpenSSH it's C:\Users\Philip\.ssh\config — same format, same directives. Create the file if it doesn't exist; SSH reads it automatically on every connection.

Before and After

Without config — type everything
ssh -p 2222 -i ~/.ssh/id_work \ philip@work.example.com ssh -i ~/.ssh/id_pi \ pi@192.168.1.101 ssh -p 22 -i ~/.ssh/id_ed25519 \ philip@192.168.1.100
With config — short aliases
ssh work ssh pi ssh server

Config File Structure

The file is a series of Host blocks. Each block starts with a Host line giving a nickname, followed by indented directives that apply to that host. The nickname is what you type in the terminal — it doesn't have to match the real hostname.

# ~/.ssh/config # Lines starting with # are comments — ignored by SSH Host server ← the nickname you type: ssh server HostName 192.168.1.100 ← real IP or hostname User philip ← login username Port 22 ← port (omit if 22) IdentityFile ~/.ssh/id_ed25519 Host pi HostName 192.168.1.101 User pi IdentityFile ~/.ssh/id_pi Host work HostName work.example.com User philip Port 2222 IdentityFile ~/.ssh/id_work
philip@debian — using config aliases
# All of these use the settings from ~/.ssh/config philip@debian:~$ ssh server philip@server:~$ philip@debian:~$ ssh pi pi@raspberrypi:~$ philip@debian:~$ ssh work philip@work:~$
Config works everywhere SSH does — the alias works with scp, sftp, and rsync too. Once pi is defined in your config, rsync -av files/ pi:~/files/ just works.

The Global Default Block

A Host * block at the end of the file sets defaults that apply to every connection unless a more specific block overrides them. Put keepalives, compression, and agent forwarding defaults here so you don't repeat them in every host block.

# ~/.ssh/config — global defaults at the END of the file # Host * must come LAST — specific blocks above take priority Host server HostName 192.168.1.100 User philip Host work HostName work.example.com User philip Port 2222 Host * ← applies to ALL connections ServerAliveInterval 60 ← keepalive every 60 seconds ServerAliveCountMax 3 ← drop after 3 missed keepalives AddKeysToAgent yes ← auto-add keys to ssh-agent IdentityFile ~/.ssh/id_ed25519 ← default key
Order matters: SSH reads the config top to bottom and uses the first matching value it finds for each directive. Put specific host blocks at the top and Host * defaults at the bottom — not the other way around.

All Useful Directives

DirectiveWhat it doesExample value
HostName The real hostname or IP to connect to 192.168.1.100
User Login username on the remote machine philip
Port Port to connect on (default 22) 2222
IdentityFile Path to the private key file to use ~/.ssh/id_work
IdentitiesOnly Only use the key specified — don't try others from the agent yes
ServerAliveInterval Send a keepalive packet every N seconds 60
ServerAliveCountMax Drop connection after N missed keepalive replies 3
AddKeysToAgent Automatically add a used key to the SSH agent yes
ForwardAgent Forward your local SSH agent to the remote machine (use with care) yes
ProxyJump Connect via a jump host (see section below) jumphost
LocalForward Forward a local port to a remote address via the tunnel 8080 localhost:80
Compression Enable compression — useful on slow connections yes
StrictHostKeyChecking How to handle unknown or changed host keys accept-new
LogLevel Verbosity of SSH output (QUIET, ERROR, INFO, VERBOSE, DEBUG) QUIET
ConnectTimeout Seconds to wait before giving up on connecting 10
ControlMaster Reuse an existing connection for new sessions (multiplexing) auto
ControlPath Socket file for connection multiplexing ~/.ssh/cm-%r@%h:%p

Wildcards and Pattern Matching

The Host keyword supports wildcards, which is useful for groups of similar hosts:

# Match all hosts ending in .example.com Host *.example.com User philip IdentityFile ~/.ssh/id_work # Match all hosts starting with pi Host pi* User pi IdentityFile ~/.ssh/id_pi # Match multiple names in one Host line (space-separated) Host server homeserver debian-box HostName 192.168.1.100 User philip

ProxyJump — Connecting Through a Jump Host

A jump host (also called a bastion host) is an intermediate machine you connect through to reach a target that isn't directly accessible — for example, a server on a private network behind a firewall, or a Raspberry Pi on your home network when you're away from home.

You
philip@laptop
SSH (port 22)
Jump Host
jumphost
public IP
SSH (internal)
Target
server
192.168.1.100

Before ProxyJump, you had to SSH into the jump host first, then SSH again to the target — two hops, two prompts, two sessions to manage. ProxyJump makes SSH handle both hops transparently in a single command.

ProxyJump in the config file

# ~/.ssh/config # The jump host — publicly accessible Host jumphost HostName jump.example.com User philip IdentityFile ~/.ssh/id_ed25519 # The target — only reachable via the jump host Host internal-server HostName 192.168.1.100 User philip IdentityFile ~/.ssh/id_ed25519 ProxyJump jumphost ← connect via jumphost automatically
philip@laptop — jumping through to internal server
philip@laptop:~$ ssh internal-server (SSH connects to jumphost, then tunnels through to 192.168.1.100) philip@internal-server:~$ ← landed on the target, one command

ProxyJump inline (without editing config)

philip@laptop — ProxyJump as a flag
philip@laptop:~$ ssh -J philip@jumphost philip@192.168.1.100 # Chain multiple jump hosts philip@laptop:~$ ssh -J hop1,hop2 philip@final-target
Your key stays on your machine. With ProxyJump, your private key is only used by your local SSH client — it never touches the jump host. This is safer than the old approach of copying your key to the jump host and using agent forwarding. Always prefer ProxyJump over ProxyCommand ssh -A for this reason.

Connection Multiplexing — One Connection, Many Sessions

Every time you open a new terminal and SSH to the same server, the full handshake runs again. Multiplexing reuses the first connection as a shared channel — subsequent connections to the same host open in milliseconds and don't require re-authentication.

# ~/.ssh/config — multiplexing settings Host * ControlMaster auto ← reuse existing connections ControlPath ~/.ssh/cm-%r@%h:%p ← socket file per connection ControlPersist 10m ← keep master alive 10 min after you exit

With this in place, the second ssh server you open in a new tab connects almost instantly — no new handshake, no passphrase prompt, because it tunnels through the existing session.

A Complete Real-World Config

This is what Philip's ~/.ssh/config might look like after setting up all the hosts covered in this course:

# ~/.ssh/config — Philip's full config # ── Home server (Debian) ───────────────────────────────────── Host server HostName 192.168.1.100 User philip IdentityFile ~/.ssh/id_ed25519 # ── Raspberry Pi ───────────────────────────────────────────── Host pi HostName 192.168.1.101 User pi IdentityFile ~/.ssh/id_pi # ── osztromok.com website server ───────────────────────────── Host osztromok HostName osztromok.com User philip IdentityFile ~/.ssh/id_ed25519 Port 22 # ── Work server (non-standard port) ────────────────────────── Host work HostName work.example.com User philip Port 2222 IdentityFile ~/.ssh/id_work # ── Internal server via jump host ──────────────────────────── Host internal HostName 192.168.10.50 User philip ProxyJump work IdentityFile ~/.ssh/id_work # ── Global defaults (must be LAST) ─────────────────────────── Host * ServerAliveInterval 60 ServerAliveCountMax 3 AddKeysToAgent yes ControlMaster auto ControlPath ~/.ssh/cm-%r@%h:%p ControlPersist 10m

Creating and Editing the Config File

philip@debian — create and protect the config file
# Create it if it doesn't exist, then edit philip@debian:~$ touch ~/.ssh/config philip@debian:~$ chmod 600 ~/.ssh/config philip@debian:~$ vi ~/.ssh/config # Verify SSH reads it correctly (prints the resolved settings for a host) philip@debian:~$ ssh -G server hostname 192.168.1.100 user philip port 22 identityfile ~/.ssh/id_ed25519 serveraliveinterval 60 ...
ssh -G hostname is the config file debugger — it prints every resolved setting for that host (including defaults inherited from Host *) without actually connecting. Use it to confirm your config is being read the way you expect.

StrictHostKeyChecking — Handle with Care

One directive worth a specific mention: StrictHostKeyChecking controls what happens when SSH sees an unknown or changed host key.

ValueBehaviourWhen to use
yes Refuse connection if key is unknown or changed. Never auto-add. Maximum security — you manually manage known_hosts.
accept-new Auto-add unknown hosts but refuse changed keys. Good balance. Recommended for most uses — protects against MITM, allows new hosts.
ask (default) Prompt for unknown hosts; refuse changed keys. The default — fine for interactive use.
no Accept any key silently — never check known_hosts. Scripting against ephemeral hosts (containers, VMs that change keys constantly). Never use on real servers.
Don't put StrictHostKeyChecking no in your global Host * block. It's tempting when you're tired of prompts, but it disables the protection that detects man-in-the-middle attacks. If you need it for scripting, set it only for the specific host or a specific IP range in its own Host block.

Quick Reference

TaskHow to do it
Create the config filetouch ~/.ssh/config && chmod 600 ~/.ssh/config
Edit itvi ~/.ssh/config
Debug / print resolved settingsssh -G server
Connect using an aliasssh server
Override a config setting inlinessh -o Port=2222 server
Jump through a host (inline)ssh -J jumphost philip@target
Use config alias with sftpsftp server
Use config alias with rsyncrsync -av files/ server:~/files/
Next — Chapter 5: Port Forwarding & Tunnelling. SSH can do much more than give you a shell — it can create encrypted tunnels that forward traffic from a local port through the SSH connection to any address the server can reach. Chapter 5 covers local forwarding, remote forwarding, dynamic (SOCKS proxy) forwarding, and practical use cases for each.