SSH keys are the standard authentication mechanism for Linux servers, Git hosting, homelab nodes, and cloud VMs. Password logins do not scale, encourage reuse, and attract brute-force bots on any port 22 exposed to the internet. A properly generated ed25519 key pair, loaded in an agent, with correct file permissions and a documented rotation plan, eliminates daily password typing while dramatically improving security posture.

This guide covers key generation, agent usage, deploying public keys to servers, hardening sshd_config, Git integration, and troubleshooting the failures you will see in real homelab deployments.

Before you begin

One key or many? Use separate keys per trust boundary: personal homelab, work, production. Comment fields help identify keys on servers (-C "fetac-laptop-2026").

Passphrase trade-off: Passphrases protect keys at rest on laptops; combine with ssh-agent to avoid typing every connection.

Never share private keys. Public keys (*.pub) go on servers; private keys stay on clients.

Backup keys securely before disk failures—password manager attachments or encrypted offline storage.

Generate ed25519 keys

ssh-keygen -t ed25519 -a 100 -C "your-label-here"
# Accept default ~/.ssh/id_ed25519 or choose custom path
# Enter passphrase when prompted (recommended)

Verify files:

ls -la ~/.ssh/id_ed25519*
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub

For legacy systems lacking ed25519 support, RSA 4096 is fallback:

ssh-keygen -t rsa -b 4096 -C "legacy-compat"

Start ssh-agent and add keys

On desktop sessions, agents often auto-start. Manual workflow:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -l    # list loaded keys

Persist agent across sessions using desktop keyring integration or ~/.bashrc snippets—avoid scripting passphrases in plaintext.

Install public key on a server

Method A: ssh-copy-id (easiest)

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@192.168.1.100

Method B: manual append

mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-ed25519 AAAA...comment" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Method C: cloud-init / Ansible / provider UI for VPS templates (Hetzner, DigitalOcean, AWS EC2 key pairs).

Test login:

ssh -i ~/.ssh/id_ed25519 user@192.168.1.100

Client config for homelab scale

~/.ssh/config reduces typing and enforces options:

Host nas
    HostName 192.168.1.50
    User admin
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Host *.example.com
    User deploy
    ForwardAgent no

Permissions:

chmod 600 ~/.ssh/config

Connect with ssh nas.

Harden server sshd

Edit /etc/ssh/sshd_config (paths vary slightly by distro):

PubkeyAuthentication yes
PasswordAuthentication no
PermitRootLogin no
ChallengeResponseAuthentication no
MaxAuthTries 3
AllowUsers deploy admin

Validate and reload:

sudo sshd -t
sudo systemctl reload sshd

Critical: Keep an existing session open while testing changes in a second terminal—lockouts happen.

Optional enhancements:

  • Change default port (security through obscurity only—pair with firewall allowlists).
  • AllowUsers / AllowGroups restrict accounts.
  • Fail2ban or firewall rate limits on SSH.

Git hosting (GitHub, GitLab, Gitea)

Copy public key:

ssh-keygen -y -f ~/.ssh/id_ed25519   # regenerate pub from private if needed
cat ~/.ssh/id_ed25519.pub

Add to provider UI → SSH keys. Test:

ssh -T git@github.com
git clone git@github.com:user/repo.git

Use separate deploy keys per repo in CI with read-only scope.

Certificates and advanced patterns (awareness)

SSH certificates (smallstep, HashiCorp Vault) scale key management for teams. For homelab, static keys plus inventory docs often suffice until you manage dozens of nodes.

Jump hosts:

Host internal
    HostName 10.0.0.5
    ProxyJump bastion

Hardware security keys (FIDO2/U2F)

YubiKey and similar devices support sk-ssh-ed25519@openssh.com keys on OpenSSH 8.2+:

ssh-keygen -t ed25519-sk -O resident -O verify-required

Resident keys store credentials on the hardware token—excellent for admin access with phishing resistance. Backup codes and secondary keys remain mandatory; losing the only hardware key without backup locks you out.

SSH hardening with 2FA (optional layer)

Combine pubkey auth with libpam-google-authenticator or Duo for high-value bastion hosts:

# /etc/pam.d/sshd — example pattern; test carefully
auth required pam_google_authenticator.so

Keep pubkey-only on internal homelab VLANs; add TOTP on internet-exposed jump boxes.

Inventory and rotation workflow

Maintain a simple spreadsheet or git-managed YAML listing:

  • Key label and creation date
  • Public key fingerprint (ssh-keygen -lf id_ed25519.pub)
  • Hosts where pubkey is deployed
  • Rotation schedule

Rotation procedure:

  1. Generate new key pair.
  2. Deploy new pubkey to all hosts (keep old key temporarily).
  3. Verify logins with new key.
  4. Remove old pubkey from authorized_keys.
  5. Revoke old key in Git hosting UI.

Automate deployment with Ansible authorized_key module at homelab scale:

- name: Deploy SSH key
  ansible.builtin.authorized_key:
    user: deploy
    key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"

Bastion host pattern for homelab

Expose only a hardened jump box to the internet; internal nodes firewall to bastion IP:

# ~/.ssh/config
Host bastion
    HostName vpn.example.com
    User jump
    IdentityFile ~/.ssh/id_ed25519

Host homelab-*
    ProxyJump bastion
    User admin
    IdentityFile ~/.ssh/id_ed25519_homelab

On internal nodes, /etc/ssh/sshd_config:

AllowUsers admin
PasswordAuthentication no
AllowTcpForwarding no

Combine with WireGuard instead of public SSH when possible—SSH becomes LAN-only over tunnel.

Logging and audit

Enable verbose auth logging temporarily during key migrations:

# Ubuntu/Debian
sudo tail -f /var/log/auth.log

# Fedora
sudo journalctl -u ssh -f

After successful migration, reduce verbosity. Consider auditd rules on bastion hosts tracking authorized_keys modifications.

ssh-agent forwarding caution

ForwardAgent yes in config enables remote hosts to use your local keys—convenient for jump chains, dangerous on untrusted servers. Prefer ProxyJump without agent forwarding when admins on intermediate boxes are not fully trusted.

ssh -A user@host   # explicit forward when needed only

Store backup key material encrypted offline; paper printouts of ed25519 public keys help during incident response when laptops are lost.

When revoking compromised keys, remove pubkeys from authorized_keys, Git hosting, CI secrets, and hardware security modules in the same maintenance window—partial revocation leaves backdoors.

For teams, enforce AuthorizedKeysFile paths and audit with configuration management rather than manual edits on each server.

Publish team onboarding docs that show exact ssh-keygen flags and forbidden practices (shared keys, keys without passphrases on laptops).

Troubleshooting

Permission denied (publickey). Wrong key, not loaded in agent, or server lacks matching pubkey. Debug:

ssh -vvv user@host

Check server /var/log/auth.log or journalctl -u ssh.

WARNING: UNPROTECTED PRIVATE KEY FILE! Fix modes: chmod 600 private key, 700 .ssh.

Agent refused operation. Key not added (ssh-add), or wrong SSH_AUTH_SOCK in sudo sessions.

Host key verification failed. MITM warning or rebuilt server—verify fingerprint out-of-band, then:

ssh-keygen -R hostname

Connection timeout. Firewall, wrong IP, or sshd not running: sudo systemctl status ssh.

Too many authentication failures. SSH client offers all keys; use IdentitiesOnly yes and specific IdentityFile.

Key takeaways

  • Generate ed25519 keys with passphrases and store private material at 600 permissions.
  • Deploy pubkeys with ssh-copy-id, then disable password authentication on servers.
  • Use ~/.ssh/config to tame homelab hostnames, users, and keys.
  • Test sshd changes in a second session before closing your last working login.
  • Separate keys per environment and document fingerprints for rotation.

FAQ

Can I use one key everywhere?
You can, but compromise blast radius increases. Split keys for prod vs lab.

How often rotate?
Annually or on personnel/device changes; rotation is easier with configuration management.

Is SSH on port 22 safe?
With keys-only and firewall allowlists, yes. Bots scan 22 constantly—fail2ban helps if passwords were ever enabled.

Ed25519 vs RSA?
Prefer ed25519 for size, speed, and modern security margins.

Can I use the same key on phone and laptop?
Use separate keys per device when possible; mobile clients often support their own key generation and export workflows.