Using ~/.ssh/config simplifies SSH connections, eliminating the need to remember and type lengthy commands for each server. By configuring host aliases, specifying usernames, defining ports, and managing SSH keys, access to multiple servers becomes more convenient and efficient.
Creating the Config File #
If SSH key authentication is already set up, the ~/.ssh directory should exist. Otherwise, create it as follows.
Verify if the ~/.ssh directory exists
#
ls -laIf ~/.ssh does not exist, create it and set the appropriate permissions:
mkdir ~/.ssh
chmod 700 ~/.sshCreate the ~/.ssh/config file
#
touch ~/.ssh/configEditing ~/.ssh/config
#
Next, add server connection details to ~/.ssh/config. A typical configuration looks like this:
# Server A
Host serverA
Hostname example.com
Port 22
User makoto
IdentityFile ~/.ssh/serverA.key
# Server B
Host serverB
Hostname 192.168.1.10
Port 50134
User makoto
IdentityFile ~/.ssh/serverB.key
# Server C
Host serverC
Hostname test-server.com
Port 48912
User makotoUnderstanding Key Parameters #
| Parameter | Description |
|---|---|
Host |
Alias for the server (used in SSH commands) |
HostName |
Server domain name or IP address |
Port |
SSH port (default: 22) |
User |
SSH login username |
IdentityFile |
Path to the private key (for key authentication) |
Additional parameters can be configured as needed, but these are the most essential ones.
Connecting to Servers #
Once the ~/.ssh/config file is set up, connecting to a server is as simple as using the alias defined in the Host field.
To connect to Server A:
ssh serverATo connect to Server B:
ssh serverBTo connect to Server C:
ssh serverCAdditional Useful SSH Config Parameters #
Beyond the basics, several advanced options further improve usability and security.
Forwarding GPG Agent via RemoteForward
#
RemoteForward forwards a local Unix socket to the remote host, enabling remote operations such as git commit -S without copying private keys.
Important #
- The first path is on the remote host
- The second path is on the local machine
- Use
StreamLocalBindUnlink yesto prevent failures if a stale socket already exists on the remote side - Socket paths differ between Linux and macOS
Socket paths #
Typical socket paths:
| Environment | Socket example |
|---|---|
| Linux (systemd) | /run/user/<UID>/gnupg/S.gpg-agent |
| macOS (GPGTools / brew) | /Users/<username>/.gnupg/S.gpg-agent |
Check your socket path:
ls -l /run/user/$(id -u)/gnupg/ 2>/dev/null
ls -l ~/.gnupg/ 2>/dev/nullExample: macOS → Linux remote #
Host remote-with-gpg
HostName dev.example.com
User makoto
StreamLocalBindUnlink yes
RemoteForward /run/user/1000/gnupg/S.gpg-agent /Users/makoto/.gnupg/S.gpg-agentStreamLocalBindUnlink yes removes an existing socket at the remote path before binding, avoiding:
bind: Address already in use
channel_setup_fwd_listener: cannot listen to path ...This setup allows signing or SSH authentication remotely while keeping private keys local.
Server-side configuration note #
While StreamLocalBindUnlink is commonly configured in the client’s ~/.ssh/config to control behavior on a per-host basis, it can also be defined on the server side to enforce the unlink-before-bind behavior for all users and all forwarded sockets.
# /etc/ssh/sshd_config
StreamLocalBindUnlink yesAfter updating the server configuration, restart the SSH daemon to apply the changes:
sudo systemctl restart sshdIn short, client-side configuration provides selective control for your own connections, while server-side configuration enforces a consistent policy for every user on that host.
Selecting Only the Intended Key with IdentitiesOnly
#
When multiple keys are loaded, SSH may attempt them all, leading to authentication failures.
IdentitiesOnly yes forces SSH to use only the key specified for that host.
Host serverA
HostName example.com
User makoto
IdentityFile ~/.ssh/serverA.key
IdentitiesOnly yesThis option is recommended when multiple SSH or GPG keys are in use.
Connecting Through a Jump Host with ProxyJump
#
ProxyJump allows connecting to a target server through an intermediate host (bastion), without chaining commands manually.
Host bastion
HostName bastion.example.com
User makoto
Host internal-server
HostName 10.0.0.50
User makoto
ProxyJump bastionAfter setting this up:
ssh internal-serverSSH automatically routes through the bastion server.
Tunneling Local Ports with LocalForward
#
LocalForward creates an encrypted tunnel from a local port to a remote service.
This is useful for securely accessing internal services such as databases or dashboards.
Host db-server
HostName internal-db.example.com
User makoto
LocalForward 15432 127.0.0.1:5432After connecting:
ssh db-server
psql -h localhost -p 15432Typical use cases include:
- accessing internal PostgreSQL / MySQL
- accessing private development services locally
- viewing dashboards without exposing them publicly
Sharing Common Settings with Host *
#
To avoid repeating configuration values across multiple hosts, you can define shared defaults using Host *.
Settings under this section apply to all hosts unless overridden later.
# Shared defaults
Host *
IdentitiesOnly yes
ServerAliveInterval 30
AddKeysToAgent yesNote on configuration order #
SSH evaluates patterns in the order they appear in the file.
Because Host * matches every host, placing it too early can unintentionally override settings that were meant to apply only to specific hosts.
Here’s an example of a recommended structure:
# Specific hosts first
Host bastion
User makoto
IdentityFile ~/.ssh/bastion.key
# Shared defaults afterwards
Host *
IdentitiesOnly yes
ServerAliveInterval 30Note on pattern matching #
Host patterns use shell-style wildcards, meaning they match partial names as well, not just exact matches. This makes selective grouping possible, for example:
# Matches any name ending in "-test"
Host *-test
User tester
IdentityFile ~/.ssh/test.key
# Matches hosts starting with "db"
Host db*
LocalForward 15432 127.0.0.1:5432
# Matches everything
Host *
IdentitiesOnly yesPattern matching is powerful, but be aware of how it interacts with ordering.
For example, the pattern *-test matches hosts such as qa-test, prod-test, and test-test.
Likewise, a definition like Host server does not automatically take precedence over Host * — it must appear later in the file to override it.
Understanding both glob-style pattern matching and order-based precedence is essential when managing many hosts. Getting this right allows you to avoid duplication, apply consistent defaults, and selectively override only where necessary.
Summary of Advanced Parameters #
| Parameter | Purpose |
|---|---|
RemoteForward |
Forward local agent/socket to remote host |
StreamLocalBindUnlink |
Remove stale socket before binding |
IdentitiesOnly |
Use only the specified key |
ProxyJump |
Connect via a bastion/jump host |
LocalForward |
Access remote services through local ports |
Host * |
Define shared defaults; order and patterns matter |