Generate a key pair, deploy it to your server, harden your sshd config β and type your passphrase only once per session.
π ~12 min readβ Linux Β· macOS Β· Windowsπ Ed25519 Β· RSA Β· ssh-agent
Table of Contents
- Why key-based SSH with a passphrase?
- Generating your key pair
- Deploying the public key to the server
- Hardening the SSH server config
- ssh-agent β type your passphrase only once
- Simplify connections with ~/.ssh/config
- Checklist & best practices
01 Why key-based SSH with a passphrase?
Password authentication is the most common target for brute-force attacks. Public key authentication replaces the password with a cryptographic pair: a private key that stays on your machine and a public key that lives on every server you want to access. Only someone holding the private key can authenticate.
Adding a passphrase to the private key encrypts it on disk. Even if someone steals your key file, it’s useless without the passphrase. You get two independent layers of security without sacrificing convenience β that’s where ssh-agent comes in.
π‘
Algorithm recommendation: Use Ed25519 β it’s faster, produces shorter keys, and offers equivalent or better security compared to RSA-4096. Only fall back to RSA if you need to connect to very old systems.
02 Generating your key pair
Generate an Ed25519 key (recommended)
Run this on your local machine β never on the server.
# -C adds a comment (usually your email) to identify the key
ssh-keygen -t ed25519 -C "you@example.com"
Fallback: RSA 4096-bit
Only use this if your target server is too old to support Ed25519.
ssh-keygen -t rsa -b 4096 -C "you@example.com"
What ssh-keygen will ask you
- File path β press Enter to accept the default (
~/.ssh/id_ed25519). Use a custom name if you manage multiple keys. - Passphrase β choose a long, memorable phrase (e.g.
MyDogIsCalledMax42!). You’ll confirm it twice.
π
Your private key (~/.ssh/id_ed25519) must never leave your machine. Only the public key (~/.ssh/id_ed25519.pub) is copied to servers.
Verify the generated files
ls -la ~/.ssh/
# You should see:
# -rw------- id_ed25519 β private key (permissions 600)
# -rw-r--r-- id_ed25519.pub β public key (safe to share)
03 Deploying the public key to the server
Recommended: ssh-copy-id
This command appends your public key to ~/.ssh/authorized_keys on the remote server and sets the correct permissions automatically.
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server
If your server runs SSH on a non-standard port:
ssh-copy-id -i ~/.ssh/id_ed25519.pub -p 2222 user@your-server
Manual method (Windows / no ssh-copy-id)
# 1. Print the public key on your local machine
cat ~/.ssh/id_ed25519.pub
# 2. On the server, paste and save it
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "PASTE_YOUR_PUBLIC_KEY_HERE" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Test the key-based login
Before touching the server config, confirm authentication by key actually works:
ssh -i ~/.ssh/id_ed25519 user@your-server
# You should be prompted for your passphrase, then logged in.
β οΈ
Keep an open session while reconfiguring the server. If you make a mistake and lock yourself out, an active session is your lifeline.
04 Hardening the SSH server config
Edit sshd_config on the server
sudo nano /etc/ssh/sshd_config
Key directives to set
# Enable public-key authentication
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
# Disable password authentication
PasswordAuthentication no
# Disable direct root login
PermitRootLogin no
# Deny empty passwords
PermitEmptyPasswords no
# (Optional) Restrict to specific users
AllowUsers youruser
Validate and restart
Always test the config file before restarting the daemon β a syntax error can make the service fail to start.
# Dry-run: check for syntax errors
sudo sshd -t
# If no output (no errors), restart
sudo systemctl restart sshd
π«
Set PasswordAuthentication no only after verifying that key-based login works. Disabling passwords before that locks you out permanently.
05 ssh-agent β type your passphrase only once
ssh-agent is a background process that holds your decrypted private key in memory. Once you unlock your key once, the agent handles all subsequent authentications transparently β no passphrase prompt on every ssh call.
Linux β start the agent and add your key
# Start the agent
eval "$(ssh-agent -s)"
# Add the key (passphrase asked once here)
ssh-add ~/.ssh/id_ed25519
# Confirm the key is loaded
ssh-add -l
Auto-start on every terminal session
Add this block to your ~/.bashrc or ~/.zshrc:
# Start ssh-agent only if not already running
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519 2>/dev/null
fi
π‘
Use ssh-add -t 28800 to set an 8-hour time-to-live on the loaded key. After that, the passphrase is required again β a good balance between security and convenience.
macOS β integrate with the system Keychain
macOS ships with a Keychain-aware ssh-agent. Add this to ~/.ssh/config:
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
Then add the key to the Keychain once:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
After this, the passphrase is remembered permanently across reboots β you’ll never be asked again.
Windows β OpenSSH (PowerShell)
# Run as Administrator
Set-Service ssh-agent -StartupType Automatic
Start-Service ssh-agent
# Add the key
ssh-add $env:USERPROFILE\.ssh\id_ed25519
Windows β PuTTY / Pageant
Launch Pageant, click Add Key, select your .ppk key file, and enter the passphrase. It stays unlocked for the whole Windows session.
Removing keys from the agent
# Remove a specific key
ssh-add -d ~/.ssh/id_ed25519
# Remove all keys
ssh-add -D
06 Simplify connections with ~/.ssh/config
The client config file lets you define aliases and per-host options so you can type ssh prod instead of a long command every time.
# ~/.ssh/config
Host staging
HostName 192.168.1.100
User alice
Port 22
IdentityFile ~/.ssh/id_ed25519
AddKeysToAgent yes
Host prod
HostName prod.example.com
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519_prod
AddKeysToAgent yes
Now connect with:
ssh staging
# Equivalent to: ssh -i ~/.ssh/id_ed25519 -p 22 alice@192.168.1.100
π‘
Set permissions correctly on your config file: chmod 600 ~/.ssh/config. SSH will refuse to read the file if it’s world-readable.
07 Checklist & best practices
| Step | Action |
|---|---|
| 01 | Generate an Ed25519 key pair with ssh-keygen and set a strong passphrase |
| 02 | Copy the public key to the server with ssh-copy-id |
| 03 | Verify key-based login works before changing anything on the server |
| 04 | Edit /etc/ssh/sshd_config β disable password auth, block root login |
| 05 | Run sudo sshd -t to validate, then systemctl restart sshd |
| 06 | Set up ssh-agent (or Keychain on macOS) to unlock once per session |
| 07 | Create ~/.ssh/config with per-host aliases and AddKeysToAgent yes |
Security best practices
- Use a unique, long passphrase for each private key.
- One key per context β separate keys for personal use, work, CI/CD pipelines.
- Back up your private key in a secure password manager (Bitwarden, 1Password, etc.).
- Set a TTL on ssh-agent with
-tso keys are flushed automatically. - Revoke compromised keys immediately by removing the relevant line from
~/.ssh/authorized_keyson every server. - Enable SSH logging on the server to detect intrusion attempts:
LogLevel VERBOSEinsshd_config.
π¨
If you forget your passphrase, the private key is gone forever. Generate a new pair and redeploy the public key to all your servers.