TL;DR: Generate separate SSH keys for different services (GitHub personal, GitHub work, VPS). Configure ~/.ssh/config with host aliases. Clone and push using the alias, not the original hostname. You get clean separation without managing credentials in code.
The Problem: One Key Isn’t Enough
Early in your career, one SSH key worked: you generated id_rsa, added it to GitHub, and every Git operation used the same key.
As you grow, you have multiple contexts:
- Personal GitHub projects
- Work GitHub organization (different account)
- VPS deployment servers
- Private repositories with restricted access
Using one key for everything means anyone with that key can access everything. It violates the principle of least privilege. Better approach: separate keys for separate purposes, configured centrally in SSH config.
This guide covers the real workflow: generating keys, testing connections, and configuring Git to use the right key automatically.
Step 1: Generate SSH Keys for Each Context
SSH keys should be named by their purpose, not by their host. Use clear naming:
# For personal GitHub projects
ssh-keygen -t ed25519 -C "personal-github" -f ~/.ssh/id_ed25519_github_personal
# For work GitHub organization
ssh-keygen -t ed25519 -C "work-github" -f ~/.ssh/id_ed25519_github_work
# For VPS/deployment server
ssh-keygen -t ed25519 -C "vps-deployment" -f ~/.ssh/id_ed25519_vps
When prompted for a passphrase, use one (it protects the key if your machine is compromised). You’ll enter it once per session due to ssh-agent.
Why ed25519? It’s modern, secure, and produces smaller keys (71 bytes vs 1700+ for RSA). If you need broader compatibility, use rsa -b 4096, but ed25519 is preferred for new setups.
Each command creates two files:
- Private key:
~/.ssh/id_ed25519_github_personal(keep secret) - Public key:
~/.ssh/id_ed25519_github_personal.pub(share with services)
Step 2: Add Public Keys to Services
Display the public key content and add it where needed:
cat ~/.ssh/id_ed25519_github_personal.pub
Copy the output and add it to GitHub (Settings → SSH Keys → New SSH Key).
Repeat for work GitHub, VPS admin panel, or wherever you need SSH access.
Important: Each service gets one public key. You don’t share private keys.
Step 3: Configure SSH Config for Automatic Key Selection
Create or edit ~/.ssh/config:
nano ~/.ssh/config
Add entries for each context. Here’s an anonymized example:
# Personal GitHub
Host github.com-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github_personal
IdentitiesOnly yes
# Work GitHub
Host github.com-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_github_work
IdentitiesOnly yes
# VPS (anonymized)
Host vps-deploy
HostName 203.0.113.42
User deploy
IdentityFile ~/.ssh/id_ed25519_vps
IdentitiesOnly yes
# Another VPS or staging server
Host staging-server
HostName 198.51.100.89
User ubuntu
IdentityFile ~/.ssh/id_ed25519_vps
IdentitiesOnly yes
Key fields:
Host: Alias you’ll use in commands (not the real hostname)HostName: Actual IP or domain nameUser: SSH username on that hostIdentityFile: Path to private key for this hostIdentitiesOnly yes: Only try the specified key (prevents SSH from trying all keys, which can cause account lockouts)
Save the file (Ctrl+O, Enter, Ctrl+X).
Set correct permissions:
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/id_ed25519_*
chmod 644 ~/.ssh/id_ed25519_*.pub
SSH is strict about permissions. Wrong modes cause “bad permissions” errors.
Step 4: Test Connections
Test SSH connection to each host:
# Test GitHub personal
ssh -T github.com-personal
# Expected output: Hi username! You've successfully authenticated...
# Test VPS
ssh vps-deploy
# Should log in or show connection successful (depends on server)
# Test with verbose output if something fails
ssh -v vps-deploy
The -v flag shows which key SSH tried and why it succeeded or failed. Useful for debugging.
Step 5: Clone Repositories Using SSH Aliases
This is where the setup pays off. Clone using the alias, not the original hostname:
# Wrong (uses default GitHub identity):
git clone git@github.com:username/personal-repo.git
# Right (uses the specific key via alias):
git clone git@github.com-personal:username/personal-repo.git
The key difference: github.com-personal instead of github.com. SSH reads ~/.ssh/config, finds the matching host alias, and uses the specified key.
For VPS:
# SSH into the server
ssh vps-deploy
# Or copy files
scp -r ./dist vps-deploy:/home/deploy/app/
Step 6: Configure Git for Multiple Accounts (Optional)
If you have multiple GitHub accounts and want different author names in commits, configure Git per directory:
# Global config (default)
git config --global user.name "Your Name"
git config --global user.email "personal@example.com"
# Work project (local config, only for this repo)
cd ~/projects/work-repo
git config user.name "Your Name (Work)"
git config user.email "work@company.com"
Verify the local config:
git config user.email # Shows local value if set, otherwise global
Step 7: Use SSH Agent to Cache Passphrases
If your keys have passphrases (they should), typing the passphrase every push is tedious.
SSH agent caches decrypted keys in memory for the session. Add to your shell config (~/.bashrc, ~/.zshrc):
# Start SSH agent if not running
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)"
# Add all keys to agent
ssh-add ~/.ssh/id_ed25519_*
fi
On first login, enter each passphrase once. For the rest of the session, SSH uses the cached key.
On macOS, the system Keychain stores SSH passphrases by default; you usually don’t need manual setup.
Troubleshooting
“Permission denied (publickey)”:
- Verify public key is added to the service (GitHub, VPS, etc.)
- Test with
-vto see which key SSH tried - Check
IdentitiesOnly yesis set in SSH config
“Bad permissions” for ~/.ssh/config or private key:
- Run the chmod commands above
- SSH requires strict ownership (must be owned by your user, not root)
SSH tries wrong key first:
- Ensure
IdentitiesOnly yesis in SSH config - Without it, SSH tries keys in order until one works, risking account lockouts
Can’t connect to GitHub but VPS works:
- SSH to GitHub uses
gituser, not your username - Verify
User gitis in the GitHub host alias
Common Workflow Example
Here’s a practical daily workflow:
# Clone personal project (uses id_ed25519_github_personal)
git clone git@github.com-personal:myusername/blog.git
cd blog
git config user.name "My Name"
git config user.email "personal@example.com"
# Make changes, commit, push
echo "New post" >> content.md
git add content.md
git commit -m "Add new post"
git push origin main
# Later: SSH into production to deploy
ssh vps-deploy
cd /app/blog
git pull origin main
npm run build
The entire flow uses SSH keys managed through config aliases. No manual credential switching, no tokens or passwords in environment variables.
SSH Key Rotation and Cleanup
Over time, you may need to rotate keys (regenerate them). When you do:
- Generate new key with same purpose name
- Update
~/.ssh/configto point to new key - Add new public key to services
- Remove old public key from services
- Delete old private key:
rm ~/.ssh/id_ed25519_old
This keeps your setup current without disrupting workflow.
Conclusion
Multiple SSH keys aren’t complicated. They’re a standard practice that improves security and convenience.
The workflow is:
- Generate keys per purpose
- Add public keys to services
- Configure SSH aliases in
~/.ssh/config - Use aliases in Git clone and SSH commands
- Let SSH agent cache passphrases
Once set up, you forget about it. Git just works, using the right key automatically. You’ve gained separation of concerns, better security, and a cleaner mental model of your credentials.