utilnx

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)

#1github.comgithub.com

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.

# Create .ssh directory if it doesn't exist
mkdir -p ~/.ssh && chmod 700 ~/.ssh
# Save the config (or append to existing file)
nano ~/.ssh/config # paste and save
# OR append to existing config:
# cat >> ~/.ssh/config # paste, then Ctrl+D
# Set correct permissions (SSH ignores configs that are too open)
chmod 600 ~/.ssh/config
# Now you can connect using the Host alias:
ssh my-server # instead of ssh user@192.168.1.100 -p 2222
git clone git@github-work:org/repo.git # uses the right key automatically

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.

# Basic usage — copies your default key
ssh-copy-id user@your-server
# Specify which key to copy
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server
# Non-standard SSH port
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 user@your-server
# Use your SSH config alias (reads host, user, port, key from config)
ssh-copy-id my-server-alias

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:

# Manual alternative (Linux/macOS)
cat ~/.ssh/id_ed25519.pub | ssh user@your-server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
# Manual alternative (Windows PowerShell)
type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh user@your-server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Pro tip: If you've set up your SSH config above, 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.

# Global defaults — put this at the bottom of your config
Host *
AddKeysToAgent yes
ServerAliveInterval 60
ServerAliveCountMax 3
Compression yes

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.

# ─── Personal GitHub ─────────────────────────────
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
# ─── Work GitHub (second account) ────────────────
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/github-work
IdentitiesOnly yes
# ─── Production VPS ───────────────────────────────
Host prod
HostName 203.0.113.42
User deploy
IdentityFile ~/.ssh/prod-deploy
Port 2222
ServerAliveInterval 60
ServerAliveCountMax 3
# ─── Internal DB server (through bastion) ────────
Host db-server
HostName 10.0.1.50
User admin
IdentityFile ~/.ssh/internal
ProxyJump prod
ForwardAgent yes
# ─── Global defaults (apply to everything) ───────
Host *
AddKeysToAgent yes
ServerAliveInterval 60
ServerAliveCountMax 3
Compression yes

Line-by-line breakdown

Personal GitHub

DirectiveWhat it does
Host github.comThe alias you use to connect. Since it matches the real hostname, "git clone git@github.com:..." just works.
HostName github.comThe actual server address. Here it's the same as the alias — but it doesn't have to be.
User gitGitHub always uses the user "git" for SSH. You identify yourself through your key, not a username.
IdentityFile ~/.ssh/id_ed25519Path to your private key. SSH will offer this key when connecting to this host.
IdentitiesOnly yesOnly 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)

DirectiveWhat it does
Host github-workA made-up alias. You'll clone work repos as: git clone git@github-work:org/repo.git
HostName github.comStill connects to github.com — the alias just tells SSH which key to use.
IdentityFile ~/.ssh/github-workA different key than your personal one. GitHub uses the key to figure out which account you are.

Production VPS

DirectiveWhat it does
Host prodShort alias. Now "ssh prod" is all you need — no IP, no port, no user to remember.
HostName 203.0.113.42The server's real IP address (or domain name). Only SSH knows about it — you just type "prod".
User deployLogs in as the "deploy" user. Pick whichever user has the right permissions on the server.
Port 2222Non-standard SSH port. Many sysadmins move SSH off port 22 to reduce automated login attempts.
ServerAliveInterval 60Sends a keep-alive ping every 60 seconds. Prevents firewalls and NATs from killing idle connections.
ServerAliveCountMax 3If 3 pings in a row get no response, SSH drops the connection cleanly instead of hanging.

Internal DB Server (through bastion)

DirectiveWhat it does
Host db-serverAlias for the internal database server that isn't directly reachable from the internet.
HostName 10.0.1.50A private IP — this server only exists inside the network. You can't SSH to it directly.
ProxyJump prodThe magic line. SSH first connects to "prod" (the VPS above), then hops from there to the DB server. One command, two jumps.
ForwardAgent yesPasses 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 *)

DirectiveWhat it does
Host *Wildcard — everything below applies to all hosts that don't have their own value for these settings.
AddKeysToAgent yesAutomatically adds your key to ssh-agent after you unlock it once. No more running ssh-add manually.
ServerAliveInterval 60Default keep-alive for all connections. Specific hosts can override this (like "prod" above, which sets the same value).
ServerAliveCountMax 3Give up after 3 missed pings. Combined with the 60s interval, that's a 3-minute timeout.
Compression yesCompresses the data stream. Barely noticeable on fast connections, but helps a lot over slow or high-latency links.
Pro tip: After saving your config, test it with 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.