SSH Config Generator
Build your ~/.ssh/config file visually. Pick a template, fill in your details, and grab the config. No more memorizing ssh -i ~/.ssh/key -p 2222 user@host every time.
Add a Host Entry
Pick a template to pre-fill, or start blank. You can add as many entries as you need.
Your Hosts(1)
Your Config
Save this as ~/.ssh/config (or append to your existing one).
How to Install
Once you've got your config, here's how to put it in the right place.
Deploy Your Public Key with ssh-copy-id
Once your config is saved and your keys are in place, use ssh-copy-id to push your public key to each server. You'll type the server password once — after that, it's key-only.
What does this do? It appends your public key to ~/.ssh/authorized_keys on the remote server and sets the correct permissions. One command, done.
No ssh-copy-id available? Windows doesn't ship it by default. Here's the manual equivalent:
ssh-copy-id prod just works — it reads the host, user, port, and key from your config. No flags needed.What Are Global Defaults (Host *)?
When you add Host * to your SSH config, the asterisk means "every connection." Any settings you put under it become your defaults — they apply to every host you connect to, unless a more specific entry overrides them.
This is the perfect spot for things you want everywhere: keep-alive pings so connections don't drop, automatic key caching so you're not retyping passphrases, or compression for slow links. Instead of repeating the same five lines in every Host block, you write them once under Host * and forget about it.
Important: SSH reads your config top-to-bottom and uses the first value it finds for each setting. That means specific Host entries should go above Host *. If you define Port 2222 in a specific host and Port 22 under Host *, the specific one wins because SSH sees it first.
Think of it like CSS specificity: specific rules beat general rules. Host * is your catch-all — the safety net at the bottom.
Real-World SSH Config Example
Here's what a production-ready ~/.ssh/config looks like when you have a personal GitHub, a work GitHub, a VPS, and a server behind a bastion host. Every line is annotated below.
Line-by-line breakdown
Personal GitHub
| Directive | What it does |
|---|---|
Host github.com | The alias you use to connect. Since it matches the real hostname, "git clone git@github.com:..." just works. |
HostName github.com | The actual server address. Here it's the same as the alias — but it doesn't have to be. |
User git | GitHub always uses the user "git" for SSH. You identify yourself through your key, not a username. |
IdentityFile ~/.ssh/id_ed25519 | Path to your private key. SSH will offer this key when connecting to this host. |
IdentitiesOnly yes | Only try this specific key — don't send every key in your agent. Prevents "too many authentication failures" errors when you have multiple keys loaded. |
Work GitHub (second account)
| Directive | What it does |
|---|---|
Host github-work | A made-up alias. You'll clone work repos as: git clone git@github-work:org/repo.git |
HostName github.com | Still connects to github.com — the alias just tells SSH which key to use. |
IdentityFile ~/.ssh/github-work | A different key than your personal one. GitHub uses the key to figure out which account you are. |
Production VPS
| Directive | What it does |
|---|---|
Host prod | Short alias. Now "ssh prod" is all you need — no IP, no port, no user to remember. |
HostName 203.0.113.42 | The server's real IP address (or domain name). Only SSH knows about it — you just type "prod". |
User deploy | Logs in as the "deploy" user. Pick whichever user has the right permissions on the server. |
Port 2222 | Non-standard SSH port. Many sysadmins move SSH off port 22 to reduce automated login attempts. |
ServerAliveInterval 60 | Sends a keep-alive ping every 60 seconds. Prevents firewalls and NATs from killing idle connections. |
ServerAliveCountMax 3 | If 3 pings in a row get no response, SSH drops the connection cleanly instead of hanging. |
Internal DB Server (through bastion)
| Directive | What it does |
|---|---|
Host db-server | Alias for the internal database server that isn't directly reachable from the internet. |
HostName 10.0.1.50 | A private IP — this server only exists inside the network. You can't SSH to it directly. |
ProxyJump prod | The magic line. SSH first connects to "prod" (the VPS above), then hops from there to the DB server. One command, two jumps. |
ForwardAgent yes | Passes your local SSH agent to the bastion, so you can authenticate on the DB server using keys stored on your laptop — without copying private keys to the bastion. |
Global Defaults (Host *)
| Directive | What it does |
|---|---|
Host * | Wildcard — everything below applies to all hosts that don't have their own value for these settings. |
AddKeysToAgent yes | Automatically adds your key to ssh-agent after you unlock it once. No more running ssh-add manually. |
ServerAliveInterval 60 | Default keep-alive for all connections. Specific hosts can override this (like "prod" above, which sets the same value). |
ServerAliveCountMax 3 | Give up after 3 missed pings. Combined with the 60s interval, that's a 3-minute timeout. |
Compression yes | Compresses the data stream. Barely noticeable on fast connections, but helps a lot over slow or high-latency links. |
ssh -T github.com or ssh -v prod (the -v flag shows exactly what SSH is doing — which config entries match, which key it tries, which port it connects to). Great for debugging.You might also need
SSH Config Explained — For Humans
What's the SSH config file?
It's a plain text file at ~/.ssh/config that acts as a shortcut book for your SSH connections. Instead of typing "ssh -i ~/.ssh/work-key -p 2222 deploy@192.168.1.50" every time, you define it once in the config, give it a name like "production", and from then on you just type "ssh production". Your fingers will thank you.
Why bother setting this up?
Three reasons: (1) You stop mistyping hostnames and ports. (2) You can use different SSH keys for different servers without juggling -i flags. (3) Tools like git, scp, rsync, and VS Code Remote SSH all read this config too — so setting it up once fixes SSH everywhere, not just your terminal.
Multiple GitHub accounts? Here's the trick
GitHub identifies you by your SSH key, so if you have personal and work accounts you can't just swap keys. The fix: create two Host entries — "github.com" (personal) and "github-work" (work) — both pointing at github.com as HostName, each with its own IdentityFile. Then clone work repos with: git clone git@github-work:org/repo.git. GitHub sees the work key and knows which account you mean. Problem solved.
What's ProxyJump?
Some servers sit behind a firewall and aren't directly reachable. You first SSH into a "bastion" or "jump" server, then SSH from there to the actual target. ProxyJump automates the double-hop. Set ProxyJump to your bastion host's alias, and SSH handles both connections transparently. One command, zero hassle.
My SSH sessions keep freezing after a few minutes
That's your firewall or NAT dropping idle connections. ServerAliveInterval sends a small keep-alive ping every N seconds to keep the connection open. Set it to 60 (one ping per minute) and ServerAliveCountMax to 3 — if the server stops responding to 3 pings in a row, SSH drops the connection cleanly instead of hanging forever. Put this in a Host * block to apply it to all connections.
What does Host * mean?
Host * is a wildcard — settings under it apply to every connection that doesn't have its own override. It's the perfect place for defaults like AddKeysToAgent, ServerAliveInterval, and Compression. Put it at the bottom of your config file. SSH reads the config top-to-bottom and uses the first match for each setting, so specific entries above Host * take priority.
Where does this file actually go?
On macOS and Linux: ~/.ssh/config. On Windows: C:\Users\YourName\.ssh\config. Create it if it doesn't exist. On Unix, make sure permissions aren't too open — run chmod 600 ~/.ssh/config. If you already have a config file, just append the new blocks at the end. Order matters: put specific Host entries above more general ones.