Anyone who works with more than one server runs into the same problem: typing out a long command like ssh -i ~/.ssh/id_rsa -p 2222 deploy@203.0.113.10 from start to finish for every connection, or digging it out of the terminal history. Once you have more than three or five servers these commands start blurring together; wasting time trying to connect with the wrong key file, the wrong port or the wrong username is all too easy. Having to remember the same information over and over for scp, rsync or a CI script can mean sending a command to the wrong server because of a single typo. The OpenSSH client offers a way to remove that repetition: the ~/.ssh/config file.
What the ~/.ssh/config file is and where it lives
~/.ssh/config is the per-user connection settings file that the OpenSSH client reads before every ssh command. On Linux and macOS it sits in the .ssh folder under the user's home directory (it can be created by hand if it does not exist); on Windows, if you use the OpenSSH client, the same file is kept at C:\Users\username\.ssh\config. The logic of the file is simple: each Host line is a pattern matched against the alias you give the ssh command (wildcards are supported too), and the options you write underneath it — HostName, User, Port, IdentityFile and so on — are applied automatically when you connect with that alias.
So instead of typing ssh -p 2222 -i ~/.ssh/id_rsa deploy@203.0.113.10, once you have defined a block like the one below, just typing ssh myserver is enough:
You can define as many Host blocks as you like in the same file. One important detail: for a given parameter the value found in the first matching block wins, and matching blocks further down the file do not override it. That is why general rules (a shared User for all servers, for example) should go at the bottom of the file and specific definitions at the top.
| Option | What it does |
|---|---|
| Host | The alias definition; the short name used in the ssh command, supports wildcards |
| HostName | The real IP address or domain |
| User | The username to connect as |
| Port | The SSH port (specified if it is not the default 22) |
| IdentityFile | The file path of the private key to use |
| ProxyJump | The target alias or user@host for connecting through a bastion/jump server |
| ServerAliveInterval / ServerAliveCountMax | Periodic keepalive packets to keep the connection alive |
These aliases are not specific to the ssh command alone. Commands such as scp file.txt myserver:/var/www/ or rsync -av folder/ myserver:/backup/ also read the same ~/.ssh/config file and automatically apply the HostName, User, Port and IdentityFile values under the myserver alias. In the same way, an SSH-based git connection such as git clone ssh://myserver/project.git benefits from these settings too. A single Host block therefore covers every kind of SSH-based connection you make to that one server.
Connecting through a bastion server with ProxyJump
In many infrastructures, database servers or servers on the internal network cannot be reached directly from the internet; you first jump to a public bastion (jump) server and from there to the target on the internal network. The method OpenSSH offers for this is ProxyJump (the config equivalent of the -J flag on the command line). In the example above, the ProxyJump bastion line in the internal-db block makes the connection go first to the server defined by the bastion alias and from there on to internal-db; there is no need to run a separate tunnel command. ProxyJump has replaced the indirect and error-prone methods once used for the same job, such as ProxyCommand ssh -W %h:%p bastion or ProxyCommand nc %h %p; it gives the same result in one line and far more readably.
File permissions: chmod 600 for the config and key files
OpenSSH strictly checks the permissions of both the ~/.ssh/config file and private key files, and this is called "strict mode". The ~/.ssh/config file must be chmod 600, meaning only its owner can read and write it; OpenSSH may reject a config file that is readable/writable by the group or other users, or silently ignore it and print a "Bad owner or permissions" warning. The same rule applies to private key files: if files such as id_rsa or id_ed25519 are readable by the group or other users, SSH refuses the connection and will not use the key.
Common mistakes
- Wrong permissions on the key file: if the private key file is left readable by the group/other users (a .pem file downloaded with default permissions, for example), SSH refuses to use that key; the fix is the command
chmod 600 key_file. - A typo in the Host alias: when you type
ssh myserver, theHostline in the config must match letter for letter; if the alias is misspelled, ssh never finds that block and tries to resolve the alias as a hostname directly, which usually gives a "could not resolve hostname" error. - Forgetting IdentitiesOnly: if there are several keys in ssh-agent or in the ~/.ssh folder, the client tries the others before the right key, and some servers drop the connection after too many failed attempts ("Too many authentication failures"). Adding
IdentitiesOnly yesto the block forces ssh to use only the IdentityFile defined for that Host. - Skipping the config file's permissions: not fixing the permissions after creating the file by hand can cause the file to be rejected by strict mode; it is a step to close off with
chmod 600 ~/.ssh/config.
These four items account for the bulk of SSH connection problems, and they usually stem from confusion over line order or indentation while editing the file by hand. Indentation is not required in the config file but is recommended for readability; what really matters is that each Host block groups the options beneath it correctly.
Generating the config file instead of writing it by hand
Rather than applying all the rules above (right line order, right option names, ProxyJump syntax) by hand for every new server, filling in the required fields and generating a ready Host block saves practical time. KEYDAL's SSH config generator asks you for the alias, HostName, User, Port, IdentityFile and an optional ProxyJump value; it then produces a copy-paste-ready Host block and the command to connect with.