SSH Keys Explained: Generation, Management & Best Practices
How SSH Key Authentication Works
SSH (Secure Shell) key authentication uses asymmetric cryptography to prove your identity without sending a password over the network. You generate a key pair consisting of a private key (kept secret on your machine) and a public key (placed on servers you want to access). The server challenges you with a mathematical problem that only your private key can solve, verifying your identity without the private key ever leaving your machine.
This is fundamentally more secure than password authentication because the private key never traverses the network, brute-force attacks are computationally infeasible against modern key algorithms, and each key pair is unique to a user-device combination. Most Git hosting platforms (GitHub, GitLab, Bitbucket) require or strongly recommend SSH keys over HTTPS password authentication.
# SSH key authentication flow:
# 1. Client connects to server
# 2. Server sends a random challenge
# 3. Client signs the challenge with its private key
# 4. Server verifies the signature using the stored public key
# 5. If valid, access is granted — no password needed
# Your key pair lives in ~/.ssh/
~/.ssh/id_ed25519 # Private key (NEVER share this)
~/.ssh/id_ed25519.pub # Public key (safe to share anywhere)Generating SSH Keys: Ed25519 vs RSA
Modern SSH supports several key algorithms, but two dominate in practice: Ed25519 (recommended) and RSA (legacy compatibility). Ed25519 uses elliptic curve cryptography to achieve equivalent security with dramatically shorter keys.
# Generate an Ed25519 key (RECOMMENDED)
ssh-keygen -t ed25519 -C "[email protected]"
# Generates: ~/.ssh/id_ed25519 and ~/.ssh/id_ed25519.pub
# Generate an RSA key (only for legacy compatibility)
ssh-keygen -t rsa -b 4096 -C "[email protected]"
# Generates: ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub
# Generate a key with a custom filename
ssh-keygen -t ed25519 -f ~/.ssh/github_key -C "github-work"
# Always set a passphrase when prompted!
# This encrypts the private key on disk| Property | Ed25519 | RSA-4096 |
|---|---|---|
| Key size | 256 bits | 4096 bits |
| Public key length | 68 characters | 560+ characters |
| Security level | ~128-bit equivalent | ~128-bit equivalent |
| Performance | Faster signing/verification | Slower |
| Compatibility | OpenSSH 6.5+ (2014) | Universal |
SSH Agent: Managing Keys in Memory
Typing your passphrase every time you use a key is tedious. The SSH agent is a background process that holds your decrypted private keys in memory, so you only enter the passphrase once per session.
# Start the SSH agent (if not already running)
eval "$(ssh-agent -s)"
# Output: Agent pid 12345
# Add your key to the agent
ssh-add ~/.ssh/id_ed25519
# Enter passphrase once, then it's cached
# On macOS, store passphrase in Keychain
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# List loaded keys
ssh-add -l
# 256 SHA256:abc123... [email protected] (ED25519)
# Remove all keys from agent
ssh-add -D
# Add key with a lifetime (auto-remove after 8 hours)
ssh-add -t 8h ~/.ssh/id_ed25519SSH Config File: Managing Multiple Keys
The SSH config file (~/.ssh/config) is the most powerful and underused SSH feature. It lets you define aliases, specify which key to use for each host, configure jump hosts, and set per-connection options.
# ~/.ssh/config
# GitHub personal account
Host github-personal
HostName github.com
User git
IdentityFile ~/.ssh/github_personal
IdentitiesOnly yes
# GitHub work account
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/github_work
IdentitiesOnly yes
# Production server with jump host (bastion)
Host production
HostName 10.0.1.50
User deploy
IdentityFile ~/.ssh/prod_key
ProxyJump bastion
# Bastion / jump host
Host bastion
HostName bastion.example.com
User admin
IdentityFile ~/.ssh/bastion_key
Port 2222
# Default settings for all connections
Host *
AddKeysToAgent yes
IdentitiesOnly yes
ServerAliveInterval 60
ServerAliveCountMax 3With this config, you can clone repositories using your aliases: git clone git@github-work:company/repo.git automatically uses the work SSH key, while git clone git@github-personal:you/repo.git uses your personal key.
Deploying Keys to Remote Servers
To use key authentication with a remote server, your public key must be added to the server's ~/.ssh/authorized_keys file. The ssh-copy-id command handles this automatically, including setting correct file permissions.
# Copy your public key to a remote server
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
# Prompts for password one last time, then key auth works
# Manual method (if ssh-copy-id is not available)
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && \
chmod 700 ~/.ssh && \
cat >> ~/.ssh/authorized_keys && \
chmod 600 ~/.ssh/authorized_keys"
# Verify it works
ssh [email protected]
# Should connect without asking for a passwordFile Permissions: The Most Common Pitfall
SSH is extremely strict about file permissions. If your key files or .ssh directory have permissions that are too open, SSH will silently refuse to use the key and fall back to password authentication. This is the single most common SSH key debugging issue. For a deeper dive into the Linux permission model, see our Linux file permissions guide.
# Required permissions (SSH will reject keys with wrong perms)
chmod 700 ~/.ssh # drwx------
chmod 600 ~/.ssh/id_ed25519 # -rw------- (private key)
chmod 644 ~/.ssh/id_ed25519.pub # -rw-r--r-- (public key)
chmod 600 ~/.ssh/authorized_keys # -rw-------
chmod 600 ~/.ssh/config # -rw-------
# Debug permission issues
ssh -vvv user@server 2>&1 | grep -i "permission\|identity\|offering"
# Look for: "Offering public key" and "Server accepts key"Security Best Practices
SSH keys are powerful authentication credentials. Treat them with the same care as passwords. A compromised private key gives an attacker access to every server that trusts it.
- Always use a passphrase -- A passphrase encrypts the private key on disk. Without it, anyone who gains access to your filesystem has your SSH credentials.
- Use Ed25519 over RSA -- Stronger security, shorter keys, faster operations, and resistance to certain attack classes.
- One key per device -- Generate a separate key pair for each machine. If a laptop is lost, revoke only that key without affecting other devices.
- Disable password authentication on servers -- Once key auth is working, set
PasswordAuthentication noin/etc/ssh/sshd_config. - Rotate keys regularly -- Generate new keys at least annually and remove old ones from authorized_keys files.
- Audit authorized_keys -- Periodically review which keys have access to each server. Remove keys for former team members immediately.
- Use SSH certificates for teams -- For organizations, SSH certificates with short lifetimes (hours) are more manageable than distributing static keys across hundreds of servers.
You can verify key fingerprints using our Hash Generator tool. SSH key fingerprints use SHA-256 hashes encoded in Base64.
Troubleshooting SSH Key Issues
When SSH key authentication fails, the verbose flag is your best debugging tool. Run ssh -vvv to see exactly what happens during the connection.
# Debug SSH connection
ssh -vvv [email protected] 2>&1 | tail -50
# Common issues and fixes:
# 1. "Permission denied (publickey)"
# → Wrong permissions on ~/.ssh/ or key files
# → Key not in server's authorized_keys
# → Wrong key being offered (check ssh -vvv output)
# 2. "Too many authentication failures"
# → Too many keys loaded in ssh-agent
# → Fix: Use IdentitiesOnly yes in ~/.ssh/config
# 3. "Host key verification failed"
# → Server's host key changed (or MITM attack!)
# → If legitimate: ssh-keygen -R server.example.com
# 4. "Agent admitted failure to sign"
# → Key not loaded in agent
# → Fix: ssh-add ~/.ssh/id_ed25519
# Check what keys the agent has loaded
ssh-add -l
# Test GitHub SSH connection
ssh -T [email protected]
# Hi username! You've successfully authenticated...SSH Keys for CI/CD Pipelines
Automated deployments and CI/CD pipelines need SSH access to servers and Git repositories. Use dedicated deploy keys with minimal permissions rather than personal SSH keys.
# Generate a deploy key (no passphrase for automation)
ssh-keygen -t ed25519 -f deploy_key -C "ci-deploy" -N ""
# GitHub Actions: Add private key as a secret
# Settings → Secrets → SSH_DEPLOY_KEY
# .github/workflows/deploy.yml
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_DEPLOY_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan server.example.com >> ~/.ssh/known_hosts
- name: Deploy
run: |
rsync -avz -e "ssh -i ~/.ssh/deploy_key" \
./dist/ [email protected]:/var/www/app/Developer Tools for Security
Generate secure passwords with our Password Generator, create UUIDs for unique identifiers with the UUID Generator, and compute hash fingerprints with the Hash Generator -- all free, all in your browser.
Open Hash GeneratorRelated Articles
Linux File Permissions
chmod, chown, and the Linux permission model explained.
Hash Functions Explained
MD5, SHA-256, bcrypt and when to use each hash algorithm.
Git Workflow Best Practices
Commits, branches, and code review patterns for teams.
Environment Variables Guide
Managing secrets and configuration across environments.