SSH Client Configuration with ~/.ssh/config
Learn how to configure reusable SSH host aliases, users, ports, keys, jump hosts, forwarding, Git identities, security settings, and troubleshooting in ~/.ssh/config.
The OpenSSH client is a command-line tool for encrypted remote login, command execution, tunneling, and file transfer. Before opening a connection, it reads client configuration and combines those settings with command-line options and built-in defaults.
The usual per-user configuration file is ~/.ssh/config. The ~ character means your home directory. This file controls how your local client connects outward. It is different from the server-side sshd_config, which controls an SSH server and is commonly maintained by an administrator.
Why use ~/.ssh/config?
Without a configuration profile, a connection may require several options:
ssh -p 2222 -i ~/.ssh/id_ed25519_app deploy@server.example.net
A Host alias stores those choices under a memorable name:
Host app-admin
HostName server.example.net
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519_app
IdentitiesOnly yes
You can then connect with:
ssh app-admin
The alias is local to your SSH client. It does not create a DNS record or change the remote server's name.
Create and secure the configuration
Create the directory and file if necessary:
mkdir -p ~/.ssh
touch ~/.ssh/config
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
Edit the file with a text editor, for example:
nano ~/.ssh/config
The directory permission 700 allows only your account to access the directory. The file permission 600 allows only your account to read and write the configuration. Restricting access matters because the file can reveal usernames, internal hostnames, key paths, forwarding rules, and connection architecture. Private keys should also be protected from unrelated local users and should normally have a passphrase.
Do not put passwords, API tokens, secret text, or private-key contents in the configuration file. Use key-based authentication and store private keys separately, such as Ed25519 keys or other supported key types.
Configuration syntax
SSH configuration uses whitespace-separated directive and value pairs. Indentation is optional but improves readability. A number sign begins a comment:
# Administrative application server
Host app-admin
HostName server.example.net
User deploy
A Host declaration starts a block. Its directives apply to the connection name being evaluated until another Host declaration begins. A block can match either an alias or a hostname pattern.
Patterns, wildcards, and negation
The wildcard * matches any sequence of characters, and ? matches one character. A pattern can cover a group of hosts:
Host *.lab.example.net
User student
ServerAliveInterval 30
A negated pattern begins with !. This lets a broad block exclude a particular name:
Host *.example.net !production.example.net
User developer
Use broad patterns carefully. Descriptive aliases and comments make long-lived configurations easier to review.
Matching and precedence
In practical terms, SSH considers explicit command-line choices before configuration-file choices, then user configuration, system-wide client configuration, and built-in defaults. Within matching configuration blocks, OpenSSH commonly uses the first value obtained for an option. Therefore, place more specific rules before broad wildcard rules, and do not assume a later block will replace an earlier value.
| Source | Examples | When it is used | Ordering consideration |
|---|---|---|---|
| Command-line options | ssh -p 2222 host, ssh -i key host | One-off or script-specific choices | Usually takes precedence over file settings |
| User configuration | ~/.ssh/config and included files | Personal reusable profiles | Specific matching blocks should precede broad ones |
| System-wide client configuration | Typically /etc/ssh/ssh_config | Defaults supplied by the operating system or administrator | Its interaction with user settings depends on the option and parsing order; inspect the effective result |
| Built-in defaults | OpenSSH defaults | When no other source supplies a value | Lowest-level fallback |
When an option appears to be ignored, use ssh -G alias rather than guessing about precedence.
Basic host profiles
The most useful directives are:
| Directive | What it controls | Typical use | Security or operational note |
|---|---|---|---|
Host | A pattern or local alias | Starts a reusable profile | Use clear names and narrow patterns |
HostName | Actual DNS name or IP address | Maps an alias to its destination | Check the address carefully for sensitive systems |
User | Remote account name | Avoids repeating user@host | Use the least-privileged suitable account |
Port | SSH service port | Stores a non-default port | A custom port is not a substitute for authentication security |
IdentityFile | Private key path | Selects a key for a host | Protect the key and do not expose its contents |
IdentitiesOnly | Whether to limit offered identities | Prevents unrelated agent keys from being tried | Useful when servers enforce authentication-attempt limits |
ProxyJump | Intermediate SSH host | Reaches private networks through a bastion | Restrict and audit jump hosts |
ServerAliveInterval | Seconds between client SSH-level probes | Detects an inactive network path | Choose a reasonable interval |
ServerAliveCountMax | Unanswered probes allowed | Determines when to close a dead session | Higher values tolerate outages but delay detection |
StrictHostKeyChecking | Handling of new or changed host keys | Controls verification behavior | Do not disable verification globally |
UserKnownHostsFile | Known-hosts database path | Uses a dedicated file for automation or temporary environments | Verify keys before trusting them |
ControlMaster | Connection multiplexing mode | Shares one connection among sessions | Protect the control socket |
ControlPath | Master connection socket path | Names the multiplexing socket | Use a private directory and a collision-resistant template |
ControlPersist | How long a master remains after logout | Speeds repeated connections | Persistent sessions have security and resource implications |
Include | Additional configuration files | Splits large configurations | Review included files as part of the configuration |
SSH keys and identity selection
An SSH key pair consists of a private key and a public key. The client proves possession of the private key; the server has previously authorized the matching public key. Never paste private-key contents into ~/.ssh/config or send them to another person.
Host work-git
HostName git.example.com
User git
IdentityFile ~/.ssh/id_ed25519_work
IdentitiesOnly yes
Host personal-git
HostName git.example.com
User git
IdentityFile ~/.ssh/id_ed25519_personal
IdentitiesOnly yes
These aliases point to the same service but select different identities. This is useful for separate organizations, accounts, or repository groups. An ssh-agent can hold decrypted keys for the client, but if it contains many keys, the server may reject the connection after too many attempts. IdentitiesOnly yes tells the client to use the identities configured for that profile instead of indiscriminately offering agent keys.
Multiple IdentityFile directives can be listed when a server may accept more than one configured identity:
Host multi-key-service
HostName service.example.net
IdentityFile ~/.ssh/id_ed25519_work
IdentityFile ~/.ssh/id_ecdsa_legacy
IdentitiesOnly yes
Connection reliability and multiplexing
ConnectTimeout limits how long the client waits while establishing a connection. It is useful for unreachable hosts and scripts:
Host app-admin
ConnectTimeout 10
ServerAliveInterval 30
ServerAliveCountMax 3
ServerAliveInterval sends an SSH-level request after the specified idle period. If the server does not respond, ServerAliveCountMax controls how many unanswered requests are tolerated. These settings can detect broken paths and reduce disconnects caused by idle network devices.
TCPKeepAlive is a lower-level TCP behavior. It uses operating-system TCP keepalive mechanisms and is distinct from SSH-level server-alive messages. Neither setting repairs an unstable network; they only help detect or maintain certain connections.
Connection multiplexing allows several SSH sessions to reuse one master connection:
Host *.example.net
ControlMaster auto
ControlPath ~/.ssh/control-%r@%h:%p
ControlPersist 10m
This can make repeated commands faster and reduce repeated authentication. Use a private control-socket location and avoid sharing it with other users. Disable multiplexing for hosts where session reuse is inappropriate.
Host-key verification and known_hosts
A host public key identifies the server endpoint. OpenSSH records trusted keys in a known_hosts file and compares them on later connections. This helps detect impersonation and man-in-the-middle attacks.
StrictHostKeyChecking controls what happens when a key is new or changes. A changed-key warning may indicate a legitimate rebuild, a changed DNS route, or an attack. Verify the new fingerprint through an independent trusted channel before replacing the stored entry. Do not routinely solve warnings by setting StrictHostKeyChecking no.
A dedicated known-hosts file can isolate automation or a temporary environment:
Host test-automation
HostName test.example.net
User runner
UserKnownHostsFile ~/.ssh/known_hosts_automation
StrictHostKeyChecking yes
Keep the trusted file protected and populate it only with verified keys. See known_hosts for the role of stored host keys.
Jump hosts and proxies
A bastion host, also called a jump host, is a controlled intermediate server used to reach an otherwise private destination. The final destination and the jump host are separate SSH connections.
Host gateway
HostName bastion.example.net
User ops
IdentityFile ~/.ssh/id_ed25519_ops
Host private-app
HostName 10.20.30.40
User deploy
ProxyJump gateway
IdentityFile ~/.ssh/id_ed25519_app
IdentitiesOnly yes
Now ssh private-app connects through gateway. Multiple hops can be expressed conceptually as a comma-separated sequence:
Host deeply-private
HostName 10.30.40.50
ProxyJump gateway,second-gateway
ProxyCommand is an older or more flexible alternative. It can invoke a helper program or custom transport when ProxyJump does not meet the requirement. Prefer the simpler directive when it is sufficient.
Forwarding and remote workflows
Forwarding creates a tunnel between listening ports. Place forwarding directives in a host profile only when they are needed:
| Type | Direction | Primary directive or command option | Example use case | Main risk |
|---|---|---|---|---|
| Local forwarding | Local port to a service reachable from the remote side | LocalForward 8080 db.internal:80 | Open a remote-only web service at local port 8080 | May expose an internal service to local users |
| Remote forwarding | Remote listening port to a service reachable locally | RemoteForward 9000 localhost:9000 | Allow a remote system to reach a local development service | Can expose the local service beyond its intended boundary |
| Dynamic forwarding | Local SOCKS proxy to destinations requested by applications | DynamicForward 1080 | Proxy browser or diagnostic traffic through the SSH host | Can become an uncontrolled proxy if exposed |
Host internal-web
HostName private-app.example.net
User deploy
ProxyJump gateway
LocalForward 8080 web.internal:80
Agent forwarding can let a remote session use keys held by your local ssh-agent without copying private keys. Enable it only for a trusted host and a specific workflow:
Host trusted-admin
ForwardAgent yes
A compromised remote account could abuse an available forwarded agent, so avoid broad Host * agent-forwarding rules. X11 forwarding is an optional legacy feature for graphical remote sessions and should likewise be enabled only when required.
File transfer and Git
scp and sftp use the same SSH client configuration, so aliases work with them:
sftp app-admin
scp ./release.tar.gz app-admin:/srv/releases/
Git SSH URLs can use an alias as the host portion. The alias selects the corresponding hostname, account, and key:
git clone git@work-git:team/project.git
In this example, Git supplies the remote account git, while work-git selects the profile. A second alias such as personal-git can select a different key for the same Git hosting domain.
Wildcard defaults and included files
Host * applies broadly, so keep its settings conservative:
Host *
ServerAliveInterval 30
ServerAliveCountMax 3
ConnectTimeout 10
Narrower patterns are safer for groups:
Host dev-*
User developer
Host *.staging.example.net
User release
Host production.example.net
User prod-admin
IdentitiesOnly yes
Put sensitive production settings in an explicit block rather than relying only on broad defaults. Because first-obtained values generally win, place the production block before a wildcard block if both can match.
Large configurations can be split into files:
Include ~/.ssh/config.d/*
For example, separate files can contain personal, work, laboratory, and infrastructure profiles. Ensure included files are readable only by the intended user when they contain sensitive topology or connection details.
Inspect, test, and debug
Test every alias before using it in automation, file transfers, or Git remotes. The -G option prints the effective configuration after matching:
ssh -G app-admin
Inspect values such as hostname, user, port, identityfile, proxyjump, and known-hosts paths. This is the fastest way to find an unexpected wildcard match or an option supplied earlier.
Verbose output shows connection and authentication decisions:
ssh -vvv app-admin
Look for the destination being contacted, matched configuration patterns, identities offered, and the reason authentication or networking failed. Do not paste private keys or sensitive diagnostic data into public reports.
| Symptom | Likely cause | How to inspect | Safe correction |
|---|---|---|---|
| Wrong remote account | Incorrect or inherited User | Run ssh -G alias | Set the intended User in a specific block |
| Wrong key offered | Many agent keys or incorrect IdentityFile | Use ssh -vvv alias and inspect effective identities | Set IdentityFile and IdentitiesOnly yes |
| Alias resolves incorrectly | Misspelled HostName or broad wildcard match | Use ssh -G alias and verbose output | Correct the profile and put specific rules before broad ones |
| Permission denied | Wrong account, key, server authorization, or key permissions | Review verbose authentication output and local permissions | Correct the account or authorized public key; protect the private key |
| Host key mismatch | Rebuilt server, changed route, or possible interception | Verify the fingerprint independently and inspect known_hosts | Replace the key only after confirmation |
| Timeout | Wrong address or port, firewall, or unavailable host | Check effective hostname, port, and verbose networking output | Correct destination settings; use a reasonable ConnectTimeout |
| Jump host failure | Invalid gateway profile, routing issue, or different key requirements | Test the gateway separately, then use verbose output for the target | Fix each connection leg and keep separate gateway and target profiles |
Common editing problems
If changes seem ignored, confirm the file is exactly ~/.ssh/config, check ownership and permissions, inspect included files, and run ssh -G. An earlier matching block or a command-line option may be supplying the value you expected to change. If sessions disconnect after a predictable idle period, investigate network or VPN behavior and consider suitable ServerAliveInterval and ServerAliveCountMax values.
Security checklist
- Use key-based authentication and protect private keys with restrictive permissions and passphrases.
- Never store passwords, tokens, or private-key material directly in the configuration.
- Verify unexpected host-key changes through an independent trusted channel.
- Do not globally disable host-key verification.
- Use
IdentitiesOnly yeswhere multiple agent keys could cause authentication confusion. - Limit agent forwarding, port forwarding, and X11 forwarding to hosts and workflows that require them.
- Use explicit settings for sensitive production systems rather than excessively broad wildcards.
- Protect multiplexing control sockets and included configuration files.
Summary
The per-user OpenSSH client file, ~/.ssh/config, turns repeated command-line options into reusable profiles. Start with Host, HostName, User, Port, and IdentityFile; add IdentitiesOnly when key selection must be strict. Use ProxyJump for bastions, forwarding directives for controlled tunnels, and keepalive or multiplexing options for appropriate operational needs. Validate profiles with ssh -G and ssh -vvv, and treat host-key verification and private-key protection as essential security controls.