~/.ssh/config Tips: Organizing Your SSH Connection Settings
What Is ~/.ssh/config?
~/.ssh/config lets you replace a long SSH command like ssh user@192.0.2.10 -i ~/.ssh/mykey -p 2222 with a simple alias: ssh myserver. Each host block stores the hostname, user, port, and key file for that connection — making it essential when managing multiple servers.
Key Takeaways
- Consolidate all long
sshcommands into named aliases in~/.ssh/config - Use
ProxyJumpfor bastion host connections in one line - Place
Host *at the end of the file to set global defaults
Prerequisites
- OS: Ubuntu / Linux (OpenSSH client)
- SSH key authentication already configured (see SSH Key Authentication Setup if not done yet)
Creating the File and Setting Permissions
If the file does not exist, create it and set the correct permissions:
touch ~/.ssh/config chmod 600 ~/.ssh/config
600 is required. If the config file is readable by the group or others, OpenSSH ignores it entirely and prints:
Bad owner or permissions on /home/user/.ssh/config
Fix with chmod 600 ~/.ssh/config.
Basic Syntax
The file consists of Host blocks, one per alias. Options inside each block are indented (four spaces is the convention).
Host <alias>
HostName <IP address or FQDN>
User <login username>
Port <port number>
IdentityFile <path to private key>OpenSSH evaluates blocks top-to-bottom and uses the first match. Later blocks can add values for options not yet set, but cannot override values already matched. This means specific entries must come before broad defaults.
Common Options
| Option | Description | Example |
|---|---|---|
HostName |
Real IP or FQDN | 192.0.2.10 |
User |
Login username | ubuntu |
Port |
Port number | 2222 |
IdentityFile |
Private key path | ~/.ssh/id_ed25519_work |
ProxyJump |
Jump host | bastion |
ServerAliveInterval |
KeepAlive interval (seconds) | 60 |
ServerAliveCountMax |
Max unanswered keepalives before disconnect | 3 |
Compression |
Enable compression | yes |
ForwardAgent |
SSH agent forwarding | yes |
AddKeysToAgent |
Auto-add key to ssh-agent | yes |
StrictHostKeyChecking |
Host key verification behavior | accept-new |
Managing Multiple Servers with Aliases
The most common use case: one block per server.
Host web01
HostName 192.0.2.10
User ubuntu
Port 22
IdentityFile ~/.ssh/id_ed25519
Host web02
HostName 192.0.2.20
User ec2-user
IdentityFile ~/.ssh/id_ed25519_aws
Host staging
HostName staging.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519_stagingAfter saving, connect with a short alias:
ssh web01 ssh staging scp file.txt staging:/home/deploy/
scp and rsync also accept the alias — no need to repeat the full connection options.
ProxyJump: Connecting Through a Bastion Host
Production environments often restrict direct SSH access — all connections must go through a bastion host. ProxyJump handles this transparently.
Host bastion
HostName bastion.example.com
User ubuntu
IdentityFile ~/.ssh/id_ed25519
Host prod01
HostName 10.0.0.10
User ubuntu
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519
Host prod02
HostName 10.0.0.11
User ubuntu
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519Connect with a single command:
ssh prod01
SSH automatically connects to bastion first, then tunnels through to prod01.
ProxyJump requires OpenSSH 7.3 or later. Check your version with ssh -V. For older versions, use ProxyCommand ssh -W %h:%p bastion as a substitute.
Multi-hop bastions (bastion1 → bastion2 → target) can be written with a comma:
Host deep-prod
HostName 10.1.0.5
User ubuntu
ProxyJump bastion1,bastion2Using Different Keys per Host
Use separate keys for GitHub, AWS, and internal servers with wildcards:
Host github.com
IdentityFile ~/.ssh/id_ed25519_github
User git
Host *.company.internal
IdentityFile ~/.ssh/id_ed25519_company
User john
Host *.amazonaws.com
IdentityFile ~/.ssh/id_rsa_aws
User ec2-userThe wildcard *.company.internal applies the same key and user to all hosts in that domain — no need to list each server individually.
Global Defaults with Host *
Host * applies to every connection. Always place it at the end of the file because OpenSSH stops evaluating after the first match for each option.
# Specific entries first
Host web01
HostName 192.0.2.10
User ubuntu
Host bastion
HostName bastion.example.com
User ubuntu
# Global defaults — always at the bottom
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519ServerAliveInterval 60+ServerAliveCountMax 3: disconnect after 180 seconds of inactivity. Prevents stuck sessions over unreliable networks.AddKeysToAgent yes: automatically loads the key intossh-agenton first use, so the passphrase is only entered once per login session.
Verifying Your Configuration
Use -G to print all resolved options for an alias without connecting:
ssh -G prod01
This shows every option that would apply, including inherited Host * defaults — useful for debugging unexpected behavior.
For a verbose trace during an actual connection:
ssh -v prod01 2>&1 | grep -E "identity|proxy|config"
Copy-Paste Template
Minimal working template
# Bastion host
Host bastion
HostName bastion.example.com
User ubuntu
IdentityFile ~/.ssh/id_ed25519
# Production server (via bastion)
Host prod01
HostName 10.0.0.10
User ubuntu
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519
# Global defaults — must be last
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
StrictHostKeyChecking accept-new