---
title: "SSH Config Cheatsheet"
slug: "ssh-config"
category: "cheatsheets"
tags: ["cheatsheets", "ssh", "cli", "reference", "security", "networking", "ops"]
status: "stable"
last_updated: 2026-05-14
summary: "SSH client config (~/.ssh/config) patterns: Host blocks, IdentityFile, ProxyJump, ControlPersist, ForwardAgent, and security hardening options."
related:
  [
    "[[ops/hostinger-vps]]",
    "[[ops/github-pages]]",
    "[[tooling/github-secrets]]",
    "[[cheatsheets/openssl-commands]]",
    "[[cheatsheets/bash-one-liners]]",
    "[[coding/shell]]",
    "[[cheatsheets/index|Cheatsheets]]",
  ]
---

## Overview

`~/.ssh/config` gives each host a named alias with its own key, port, and proxy settings. Without it, every connection requires repeating flags on the command line. This card covers the directives that matter most and the patterns that solve common problems. For key generation commands, see [[cheatsheets/openssl-commands]].

File permissions matter: `chmod 600 ~/.ssh/config` and `chmod 700 ~/.ssh/`.

## Host block structure

Each block starts with `Host <alias>` and applies to any `ssh <alias>` invocation.

| Directive | Example | What it does |
| --- | --- | --- |
| `Host` | `Host myserver` | Block label; also the alias you type. Accepts `*` and `?` wildcards. |
| `HostName` | `HostName 1.2.3.4` | Real hostname or IP to connect to. |
| `User` | `User deploy` | Remote username; avoids `user@host` typing. |
| `Port` | `Port 2222` | Non-default port. |
| `IdentityFile` | `IdentityFile ~/.ssh/myserver_ed25519` | Path to the private key. |
| `IdentitiesOnly` | `IdentitiesOnly yes` | Use only the listed key; skip the agent's other keys. |

```
Host myserver
  HostName 1.2.3.4
  User deploy
  Port 2222
  IdentityFile ~/.ssh/myserver_ed25519
  IdentitiesOnly yes
```

## ProxyJump (bastion / jump hosts)

| Directive | Example | Effect |
| --- | --- | --- |
| `ProxyJump` | `ProxyJump bastion` | Connect through the named jump host. Chains to another `Host` block. |
| `ProxyJump` (chain) | `ProxyJump bastion1,bastion2` | Hop through two bastions in order. |
| `ProxyCommand` | `ProxyCommand ssh -W %h:%p bastion` | Legacy fallback; prefer `ProxyJump`. |

```
Host bastion
  HostName bastion.example.com
  User admin
  IdentityFile ~/.ssh/bastion_ed25519

Host internal-db
  HostName 10.0.1.50
  User postgres
  ProxyJump bastion
  IdentityFile ~/.ssh/internal_ed25519
```

`ssh internal-db` automatically tunnels through the bastion without any extra flags.

## ControlPersist (connection reuse)

| Directive | Example | Effect |
| --- | --- | --- |
| `ControlMaster` | `ControlMaster auto` | Create or reuse a multiplexed connection. |
| `ControlPath` | `ControlPath ~/.ssh/ctrl/%r@%h:%p` | Socket file path for the master connection. |
| `ControlPersist` | `ControlPersist 10m` | Keep master alive 10 minutes after last client disconnects. |

```
Host *
  ControlMaster auto
  ControlPath ~/.ssh/ctrl/%r@%h:%p
  ControlPersist 10m
```

Create the directory first: `mkdir -p ~/.ssh/ctrl`. Subsequent `ssh` and `scp` to the same host reuse the existing socket with no handshake overhead.

## Security and agent directives

| Directive | Recommended value | Reason |
| --- | --- | --- |
| `AddKeysToAgent` | `yes` | Load key into `ssh-agent` on first use; avoids repeated passphrase prompts. |
| `ForwardAgent` | `no` (default) | Only enable for trusted intermediate hosts. Agent forwarding exposes your agent socket on the remote. |
| `ServerAliveInterval` | `60` | Send keepalive every 60 s to prevent idle disconnects. |
| `ServerAliveCountMax` | `3` | Disconnect after 3 missed keepalives. |
| `StrictHostKeyChecking` | `accept-new` | Accept new host keys but reject changed ones. Safer than `no`. |
| `HashKnownHosts` | `yes` | Hash hostnames in `known_hosts`; limits exposure if the file leaks. |

```
Host *
  AddKeysToAgent yes
  ForwardAgent no
  ServerAliveInterval 60
  ServerAliveCountMax 3
  StrictHostKeyChecking accept-new
  HashKnownHosts yes
```

## Wildcard and include patterns

| Pattern | Example | Effect |
| --- | --- | --- |
| Wildcard host | `Host *.example.com` | Apply directives to all subdomains. |
| Negation | `Host * !bastion` | Apply to all except the bastion. |
| `Include` | `Include ~/.ssh/work_config` | Source another config file; useful for work/personal split. |
| `Match` blocks | `Match exec "test -f ~/.work"` | Conditional directives based on shell command exit code. |

```
# Personal defaults
Host *
  IdentityFile ~/.ssh/personal_ed25519

# Work overrides (loaded last, so they win for work hosts)
Include ~/.ssh/work_config
```

Order of evaluation: first match wins for each directive, so put specific blocks before wildcards.

## Common gotchas

- Permissions must be exact. `~/.ssh/config` must be `600`; the directory must be `700`. SSH ignores the config or refuses the key otherwise.
- `ForwardAgent yes` in a `Host *` block forwards your agent to every server you touch. A compromised server can use your agent socket to impersonate you. Enable it per host, not globally.
- `ControlPersist` can hide failures. If the master connection died, subsequent `ssh` calls fail with a cryptic socket error. Run `ssh -O exit <alias>` to close a stale master.
- `IdentitiesOnly yes` prevents SSH from trying all keys in the agent. Without it, a server with `MaxAuthTries 3` locks you out before the right key is tried if the agent holds many keys.
- `Include` paths do not expand `~` in all SSH versions. Use the full absolute path or rely on the `%d` token (`%d/.ssh/work_config`).
- `HashKnownHosts yes` makes `ssh-keygen -R hostname` unable to remove the entry by hostname. Use `ssh-keygen -R <ip>` or edit `known_hosts` directly.

## Related

- [[ops/hostinger-vps]]
- [[ops/github-pages]]
- [[tooling/github-secrets]]
- [[cheatsheets/openssl-commands]]
- [[cheatsheets/bash-one-liners]]
- [[coding/shell]]
- [[cheatsheets/index|Cheatsheets]]
