The SSH Config File
If you connect to servers a lot, you quickly get tired of typing long commands like ssh -i ~/.ssh/work_key -p 2222 [email protected]. The SSH config file fixes this. It is a plain text file where you save a short nickname (called an alias) for each server, along with all its connection settings. After that you just type ssh myserver and SSH fills in the rest. This is one of the biggest quality-of-life wins in DevOps, and it costs you five minutes to set up.
SSH (Secure Shell) is the standard tool for logging into a remote Linux machine over an encrypted connection. The config file we are talking about lives in your home directory, not on the server. It controls how your computer (the client) behaves when you run the ssh command.
Where the file lives
On Ubuntu (and macOS, and any Linux), the per-user SSH config file is at ~/.ssh/config. The ~ is shorthand for your home directory, so the full path is usually /home/yourname/.ssh/config.
The file does not exist by default. You create it yourself:
mkdir -p ~/.ssh
touch ~/.ssh/config
chmod 600 ~/.ssh/config
The chmod 600 is important. It means “only the owner can read and write this file.” SSH refuses to use a config file that other users on the machine can read or edit, because that would be a security hole.
Security tip: Never put passwords in
~/.ssh/config. The file is plain text. Use SSH keys instead (see the SSH key authentication page). The config file points to your key withIdentityFile; it never contains the secret itself.
A single-host example
Open the file in any editor:
nano ~/.ssh/config
Add a block like this:
Host webserver
HostName 203.0.113.40
User deploy
Port 22
IdentityFile ~/.ssh/id_ed25519
Save and exit. Now instead of the long command, you simply run:
ssh webserver
SSH reads the block, sees that webserver maps to the real address 203.0.113.40, logs in as the deploy user on port 22, and uses the key file you specified. That is the whole idea.
What each option means
| Option | What it does | Example value |
|---|---|---|
Host | The alias (nickname) you type after ssh. | webserver |
HostName | The real IP address or domain name of the server. | 203.0.113.40 or web.example.com |
User | The Linux username to log in as. | deploy |
Port | The TCP port SSH listens on (22 is the default). | 22 or 2222 |
IdentityFile | Path to the private key used to authenticate. | ~/.ssh/id_ed25519 |
Indentation under Host is just for readability; SSH does not require it. Lines starting with # are comments and are ignored.
A multi-host example
The real power shows up when you manage several servers. You list one Host block per machine:
# Production web server
Host prod
HostName 203.0.113.40
User deploy
Port 22
IdentityFile ~/.ssh/prod_ed25519
# Staging server (custom SSH port)
Host staging
HostName staging.example.com
User ubuntu
Port 2222
IdentityFile ~/.ssh/staging_ed25519
# A database box you reach with a different key
Host db
HostName 10.0.0.15
User postgres
IdentityFile ~/.ssh/db_ed25519
# Settings applied to every host (must go last)
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Now ssh prod, ssh staging, and ssh db each connect to the right machine with the right key, user, and port. No flags to remember.
The Host * block at the bottom uses a wildcard (* means “match everything”). The two ServerAlive settings tell your client to send a tiny keep-alive packet every 60 seconds so the connection does not drop while you step away from the keyboard.
Gotcha: SSH applies the first matching value for each option, reading top to bottom. Always put specific hosts first and the catch-all
Host *block last, or your general settings will shadow the specific ones.
Jump hosts with ProxyJump
Many production servers are not reachable directly from the internet. They sit on a private network (an internal-only network with no public address). To reach them you first connect to a bastion or jump host (a single hardened server that is exposed to the internet on purpose and acts as a controlled gateway). You hop through it to get to the real target.
Doing this by hand is awkward. The ProxyJump option automates the hop:
# The public gateway
Host bastion
HostName bastion.example.com
User ubuntu
IdentityFile ~/.ssh/bastion_ed25519
# A private box reachable only through the bastion
Host internal-app
HostName 10.0.1.20
User deploy
IdentityFile ~/.ssh/app_ed25519
ProxyJump bastion
Now this single command transparently logs into the bastion and then forwards you to the private 10.0.1.20 box:
ssh internal-app
Output:
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-45-generic x86_64)
Last login: Mon Jun 15 09:12:41 2026 from 10.0.0.5
deploy@internal-app:~$
You can even chain jumps with commas, e.g. ProxyJump bastion,inner-gateway. The same alias works for scp and rsync too, so file transfers also flow through the bastion automatically.
When to use this: any time your real servers live behind a bastion, a VPN entry point, or in a private subnet. When NOT to: if your target has a direct public IP you can reach, a plain Host block is simpler.
Testing your config
To see exactly what settings SSH will use for an alias without actually connecting, run:
ssh -G prod
Output:
host 203.0.113.40
user deploy
port 22
identityfile ~/.ssh/prod_ed25519
serveraliveinterval 60
serveralivecountmax 3
This is the fastest way to debug a typo or a wrong key path.
Best practices
- Keep
~/.ssh/configat permission600and the~/.sshdirectory at700, or SSH may refuse to use them. - Use one private key per environment (prod, staging, db) so a single leaked key has limited blast radius.
- Prefer modern Ed25519 keys (
ssh-keygen -t ed25519) over older RSA keys. - Put specific
Hostblocks first and theHost *catch-all last so order-of-precedence works in your favour. - Add a comment line above each block describing what the server is for; future-you will thank you.
- Never store passwords in the file; rely on key-based authentication only.
- Use
ProxyJumpinstead of saving rawssh -Wcommands so file transfer tools inherit the same path.