SSH keys are the foundation of secure access for most developers — servers, GitHub, deployment pipelines, CI systems. But generating a key and pasting it somewhere is only half the story. Poor key hygiene — sharing keys, reusing them across contexts, never rotating them, leaving them without passphrases — turns a strong security mechanism into a liability.

Key Types: Ed25519 vs RSA

For new keys, always use Ed25519. It’s based on modern elliptic-curve cryptography, produces smaller keys, and is faster than RSA while being equally or more secure at comparable key lengths.

# Generate an Ed25519 key — the modern default
$ ssh-keygen -t ed25519 -C "your@email.com"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/user/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase): ****
Enter same passphrase again: ****
Your identification has been saved in /home/user/.ssh/id_ed25519
Your public key has been saved in /home/user/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:AbCdEf1234567890AbCdEf1234567890AbCdEf12345 your@email.com

If you must use RSA for legacy systems that don’t support Ed25519, use at least 4096 bits:

$ ssh-keygen -t rsa -b 4096 -C "your@email.com"

Always set a passphrase. A passphrase encrypts your private key on disk — if someone steals your laptop or the key file, they still can’t use it without the passphrase. Use ssh-agent so you only type it once per session.

Organizing Multiple Keys

Most developers eventually accumulate keys for different contexts: personal GitHub, work GitHub, multiple servers, deployment accounts. Keep them organized:

~/.ssh/
├── config                    # Connection routing rules
├── id_ed25519                # Default personal key
├── id_ed25519.pub
├── work_github               # Work GitHub key
├── work_github.pub
├── prod_server               # Production server key
├── prod_server.pub
└── known_hosts

The ~/.ssh/config file maps hosts to keys so you never need to specify -i:

# Personal GitHub
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

# Work GitHub account
Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/work_github
    IdentitiesOnly yes

# Production server
Host prod
    HostName 203.0.113.10
    User deploy
    IdentityFile ~/.ssh/prod_server
    Port 2222
    IdentitiesOnly yes

IdentitiesOnly yes prevents SSH from trying all loaded keys — it only offers the specified one. This matters when you have many keys loaded in the agent.

With this config, cloning a work repo is just:

$ git clone git@github-work:your-org/repo.git

SSH Agent: One Passphrase Per Session

ssh-agent holds decrypted private keys in memory so you don’t retype your passphrase on every connection.

# Start the agent (usually automatic on macOS/modern Linux)
$ eval "$(ssh-agent -s)"
Agent pid 12345

# Add your key — enter passphrase once
$ ssh-add ~/.ssh/id_ed25519
Enter passphrase for /home/user/.ssh/id_ed25519: ****
Identity added: /home/user/.ssh/id_ed25519 (your@email.com)

# List loaded keys
$ ssh-add -l
256 SHA256:AbCdEf... your@email.com (ED25519)

# Remove all keys from agent (on shared/untrusted machines)
$ ssh-add -D

On macOS, add keys to the Keychain so they persist across reboots:

$ ssh-add --apple-use-keychain ~/.ssh/id_ed25519

And in ~/.ssh/config:

Host *
    AddKeysToAgent yes
    UseKeychain yes        # macOS only

Installing Public Keys on Servers

The public key (.pub) is what you share. The private key never leaves your machine.

# Install your public key on a remote server
$ ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server.example.com

# Or manually
$ cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Verify permissions — SSH is strict about these
$ ssh user@server "chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys"

Check the server’s authorized_keys format — each line is one public key:

$ ssh user@server "cat ~/.ssh/authorized_keys"
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... personal laptop
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... work laptop
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... CI/CD system

Labeling keys with their origin (-C "personal laptop") makes auditing much easier.

GitHub SSH Setup

# Add your public key to GitHub
$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... your@email.com

# Copy that output, then go to:
# GitHub → Settings → SSH and GPG keys → New SSH key

# Test the connection
$ ssh -T git@github.com
Hi username! You've successfully authenticated, but GitHub does not provide shell access.

For multiple GitHub accounts (personal + work), use the Host alias from your config:

# Personal
$ git clone git@github.com:personal-user/repo.git

# Work (uses the github-work alias pointing to work_github key)
$ git clone git@github-work:work-org/repo.git

Key Rotation

Keys should be rotated periodically — especially for service accounts and CI/CD systems.

# Generate a new key
$ ssh-keygen -t ed25519 -C "deploy-key-$(date +%Y-%m)" -f ~/.ssh/new_deploy_key

# Add the new public key to the server's authorized_keys
$ ssh-copy-id -i ~/.ssh/new_deploy_key.pub deploy@prod.example.com

# Verify the new key works BEFORE removing the old one
$ ssh -i ~/.ssh/new_deploy_key deploy@prod.example.com "echo 'new key works'"
new key works

# Remove the old key from authorized_keys
$ ssh deploy@prod.example.com "sed -i '/old-key-fingerprint/d' ~/.ssh/authorized_keys"

# Remove the old key locally
$ rm ~/.ssh/old_deploy_key ~/.ssh/old_deploy_key.pub

Always verify the new key works before revoking the old one.

Hardening the SSH Server

On servers you control, tighten the SSH daemon config (/etc/ssh/sshd_config):

# Disable password authentication entirely — keys only
PasswordAuthentication no
ChallengeResponseAuthentication no

# Disable root login
PermitRootLogin no

# Only allow specific users
AllowUsers deploy alice bob

# Use a non-standard port (reduces automated scanning noise, not real security)
Port 2222

# Disable unused auth methods
PubkeyAuthentication yes
KerberosAuthentication no
GSSAPIAuthentication no
# Reload after changes
$ sudo systemctl reload sshd

# Test config before reloading (catches syntax errors)
$ sudo sshd -t

Key Principles

  • One key per machine, not per service. Each device you use should have its own key. If a device is lost, revoke that device’s key — not all your access.
  • Different keys for different trust levels. Personal GitHub, work GitHub, and production servers should each have their own key.
  • Never share private keys. If a teammate needs access to a server, they generate their own key and you add their public key.
  • Store backups securely. Back up private keys to an encrypted password manager (1Password, Bitwarden) or encrypted external storage — not plain cloud drives.

Conclusion

Good SSH key management is straightforward once you have a system: Ed25519 keys with passphrases, ssh-agent for convenience, ~/.ssh/config to organize multiple keys, and a rotation plan for service accounts. The most common mistake is sharing private keys or using a single personal key for everything — one key per device and one key per context makes revocation clean and limits blast radius if a key is ever compromised.