Linux · OpenSSH guide + generator

Set up an SSH config alias for every host in ~/.ssh/config

Four lines in ~/.ssh/config replace a port, a user name and an IP address with one short word. The same alias then works for scp, rsync and git, as long as the order of the blocks is right.

LineLinux CLI
Read8 min · 8 stops
DatePublished Jun 17, 2013 · updated Nov 05, 2026
Tested withOpenSSH_9.6p1 Ubuntu-3ubuntu13.20 (OpenSSL 3.0.13), git 2.43.0, rsync 3.2.7 on Ubuntu 24.04.4 LTS; permission checks also on OpenSSH_10.0p2 (Debian 13)
Tunnel bridge with light trails leading to a glass tower (AI-generated image)
AI-generated image
Guide8 stations on the Linux line

Typing ssh -p 2222 [email protected] once is fine. Typing it twenty times a day, next to other servers with their own ports and user names, is where mistakes start. An ssh config alias stores all of that under one short name, so the command becomes ssh web. Nothing runs in the background: OpenSSH reads ~/.ssh/config every time it starts.

Every ssh config file example below was resolved with OpenSSH 9.6p1 using ssh -G, which prints the settings ssh would use without connecting anywhere.

SSH config generator: build a Host block

Fill in the alias and the connection details, and the generator writes the matching block for ~/.ssh/config, together with the long command it replaces, the short one, an scp line and the ssh -G check to run before the first connection. Port 22 is left out, since it is the default. Everything happens in the browser; nothing you type is sent anywhere.

The second part checks a file mode against the rules behind "Bad owner or permissions", tested on a real file with OpenSSH 9.6p1 and 10.0p2. ssh refuses ~/.ssh/config only when someone else can write it, but ignores a private key if the group or others hold any permission bit at all.

SSH config generator

runs in your browser · nothing is sent

~/.ssh/config
Replaces this command
Connect
Copy a file
Check before connecting

Will ssh accept the file?

An ssh config file example with Host, HostName, User and Port

Create the file with the mode ssh expects:

shell
$ touch ~/.ssh/config
$ chmod 600 ~/.ssh/config

In this ssh config file example, each entry starts with a Host line naming the alias. The indented lines below it apply until the next Host line; ssh ignores the indentation itself.

~/.ssh/config
Host web
    HostName 203.0.113.10
    User alice
    Port 2222

HostName is the real address, either an IP or a DNS name. User and Port are what you would otherwise type with user@ and -p. Now ssh web does the same as the long command, and ssh -G confirms it:

output
$ ssh -G web | grep -E '^(hostname|user|port) '
user alice
hostname 203.0.113.10
port 2222

Keywords are case-insensitive, and ssh -G prints them in lower case. The alias is not: ssh -G WEB matched no block, fell back to port 22 and the local user name, and used the typed name itself, lowercased to web, as the address.

Glass security turnstiles in an empty lobby at night (AI-generated image)
AI-generated image

SSH config IdentityFile: one key per host

Without an ssh config IdentityFile line, ssh tries a list of default key names. In the ssh -G output for web, OpenSSH 9.6 listed ~/.ssh/id_rsa, id_ecdsa, id_ecdsa_sk, id_ed25519, id_ed25519_sk, id_xmss and id_dsa. Naming the key saves those attempts.

The classic case is two GitHub accounts, personal and work, each with its own key. GitHub identifies the account by the key, so a second ssh config alias that points at the same HostName does the job:

~/.ssh/config
Host github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_personal
    IdentitiesOnly yes

Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_work
    IdentitiesOnly yes
output
$ ssh -G github-work | grep -E '^(hostname|user|identityfile|identitiesonly) '
user git
hostname github.com
identitiesonly yes
identityfile ~/.ssh/id_ed25519_work

Leave out IdentitiesOnly yes and the default no applies: ssh then also offers every key loaded in ssh-agent. If the agent holds the personal key and offers it first, GitHub accepts it and the push lands in the wrong account. Servers also stop accepting attempts after a limit, so an agent full of keys can end in "Too many authentication failures" before the right key is tried.

The git remote URL uses the alias

Git only needs the ssh alias host name in place of github.com:

shell
$ git clone git@github-work:example-org/app.git
$ git remote set-url origin git@github-work:example-org/app.git

Git hands git@github-work to ssh unchanged (the table in the scp section shows the exact arguments), and ssh does the lookup. If Git itself is still new, the Githug practice game walks through remotes and pushes level by level.

Wildcard host patterns and Host * defaults

Host accepts patterns. * matches any run of characters and ? exactly one, so one block can cover a whole group of machines:

~/.ssh/config
Host *.example.com
    User deploy
    Port 2200

Host *
    ServerAliveInterval 60
    AddKeysToAgent yes
output
$ ssh -G app1.example.com | grep -E '^(hostname|user|port|serveraliveinterval) '
user deploy
hostname app1.example.com
port 2200
serveraliveinterval 60

Without a HostName, the name you typed is the address. Host * matches everything and holds defaults such as a keep-alive every 60 seconds.

Host order: the first match wins

ssh reads the file from the top, and for each option it keeps the first value it finds. Later blocks can add options that are still unset, but they cannot change one that is already set. The ssh_config manual draws the conclusion itself: host-specific declarations near the beginning, general defaults at the end.

A two-block test file shows the effect:

order.conf
Host *
    User fallback

Host web
    HostName 203.0.113.10
    User alice
output
$ ssh -G -F order.conf web | grep -E '^(user|hostname) '
user fallback
hostname 203.0.113.10

HostName from the web block still applies, because Host * did not set one. User alice is silently ignored. Command-line options sit above the whole file: ssh -G -l bob -p 22 web reported user bob and port 22.

ProxyJump: reach a host behind another

A database server that only accepts connections from the web server can still get its own ssh alias host entry. ProxyJump names the hop, and it can be another alias:

~/.ssh/config
Host db
    HostName 10.0.0.5
    User postgres
    ProxyJump web
output
$ ssh -G db | grep -E '^(hostname|user|proxyjump) '
user postgres
hostname 10.0.0.5
proxyjump web

ssh db then logs in to web (port 2222, user alice) and opens a forwarded connection to 10.0.0.5 from there. Several hops are separated by commas.

Corridor between server racks behind glass doors (AI-generated image)
AI-generated image

scp with ssh config alias, rsync and other tools

Every tool that runs the ssh binary picks up an ssh config alias for free, so scp with ssh config alias names needs no extra setup. With a stand-in for ssh that prints its arguments, the three common cases looked like this:

commandwhat ssh received
scp notes.txt web:/tmp/-x ... -s -- web sftp
rsync -a conf.d web:backup/web rsync --server ...
git ls-remote git@github-work:example-org/app.gitgit@github-work git-upload-pack ...

Since OpenSSH 9.0, scp transfers over SFTP by default, which is why ssh received -s ... sftp; the alias works the same with the legacy protocol. GUI clients and libraries with their own SSH code are a different matter, and many never read ~/.ssh/config.

Permissions, Include and debugging ~/.ssh/config

chmod 600 ~/.ssh/config is the mode to use. If others can write the file, ssh refuses it with "Bad owner or permissions", and every alias fails at once.

Long configs can be split with Include:

~/.ssh/config
Include ~/.ssh/config.d/*.conf

Host web
    HostName 203.0.113.10

In the test, a lab alias defined only in an included file resolved normally. Keep the Include above the first Host line; written further down, it belongs to the block above it and is read only when that block matches.

One edge case ssh -G db does not reveal: according to the ssh manual, command-line options such as ssh -i other_key db apply to the destination, not to the jump host. The hop to web still follows the web block, so a jump host that needs a special key has to get it in the file.

Last stops · FAQ

Questions people ask

Where is the ssh config file on Linux?

The per-user file is ~/.ssh/config; it does not exist until you create it. OpenSSH also reads the system-wide /etc/ssh/ssh_config, which on Debian and Ubuntu starts with Include /etc/ssh/ssh_config.d/*.conf. Your own file is read first, and because the first value found for each option wins, settings in ~/.ssh/config take precedence over the system defaults.

Does an ssh alias work with scp and rsync?

Yes. scp with ssh config alias names works because scp, sftp and rsync start the ssh program and pass the alias through unchanged, so ssh resolves it from the same config. scp report.pdf web:/tmp/ and rsync -a site/ web:site/ both pick up the host name, user and port of the web block. The same is true for git remotes such as git@github-work:example-org/app.git.

How do you use two GitHub accounts with different SSH keys?

Give each account its own Host alias with its own IdentityFile, add IdentitiesOnly yes and point HostName at github.com. Then use the alias in the remote URL: git@github-work:example-org/app.git. IdentitiesOnly keeps ssh-agent from offering the other account's key first.

Why does an ssh config option not take effect?

Almost always because an earlier block already set it. ssh keeps the first value it finds for each option, so a Host * block at the top wins over everything below it. ssh -G alias prints the values ssh will actually use, and command-line options such as -p and -l beat the file.

What permissions does ~/.ssh/config need?

The ssh manual requires that the file be readable and writable by you and not writable by others; it may be group-writable only if the group contains just your user. chmod 600 ~/.ssh/config satisfies that. If the permissions are too open, ssh refuses to use the file with a Bad owner or permissions error, and every alias stops working at once.

Can an ssh config file include other files?

Yes, with Include, which accepts glob patterns such as Include ~/.ssh/config.d/*.conf and loads the matching files in lexical order. Relative paths are taken from ~/.ssh. Put the line at the top of the file, because an Include placed after a Host line belongs to that block.

Next stop

Where this line continues