SSH keys use public-key cryptography. You have two files: a private key (kept by you) and a public key (placed on the server). No password is ever sent over the network.
Generating keys (Linux / Mac)
# Modern Ed25519 algorithm (recommended)
ssh-keygen -t ed25519 -C "your@email.com"
# Or RSA-4096 for compatibility
ssh-keygen -t rsa -b 4096 -C "your@email.com"
# The keys are saved to ~/.ssh/
# ~/.ssh/id_ed25519 ← private (do NOT share with ANYONE)
# ~/.ssh/id_ed25519.pub ← public (can be placed anywhere)
Adding the key to the server
# Automatically (the easiest way)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server-ip
# Or manually:
cat ~/.ssh/id_ed25519.pub | ssh user@server-ip "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Generating keys on Windows (PuTTYgen)
- Download PuTTYgen from the official PuTTY site
- Select EdDSA or RSA (4096 bits)
- Click Generate and move the mouse
- Save the private key (Save private key)
- Copy the public key from the text field at the top
- Add it to
~/.ssh/authorized_keyson the server
Disabling password login
sudo nano /etc/ssh/sshd_config
# Change or add:
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
sudo systemctl restart sshd
Common SSH errors explained
Most key-related problems fall into five errors. The exact text tells you where to look.
Permission denied (publickey)— the server accepted the connection, but none of the offered keys matched. Causes: wrong key file, the key was never added to the right user'sauthorized_keys, or ssh-agent is offering the wrong key.Too many authentication failures— the client tried more keys than the server'sMaxAuthTriesallows before it got to the right one. Point to the key explicitly:ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 user@server-ip.WARNING: UNPROTECTED PRIVATE KEY FILE!— the private key file's permissions are wider than600, so ssh refuses to read it. Fix withchmod 600 ~/.ssh/id_ed25519.Agent admitted failure to sign using the key— the key is registered inssh-agent, but the actual file on disk was deleted, renamed, or is no longer readable.REMOTE HOST IDENTIFICATION HAS CHANGED— the server's fingerprint in~/.ssh/known_hostsno longer matches the current one. If the server wasn't reinstalled and the IP wasn't reassigned to another host, stop and investigate instead of deleting the line right away.
Ed25519, RSA, and ECDSA: which algorithm to pick
The key type is set with the -t flag at generation time. The difference is more than a name.
| Algorithm | Key length | Operation speed | Compatibility |
|---|---|---|---|
| Ed25519 | 256 bits (fixed) | High | OpenSSH 6.5+ (2014 and later) |
| RSA-4096 | 4096 bits | Moderate when signing | Works everywhere, including old systems and panels |
| RSA-2048 | 2048 bits | High | Minimum acceptable length, use only under server constraints |
| ECDSA | 256-521 bits | High | Widely supported, but cryptographers recommend avoiding it due to the NIST curves used |
For new servers and your own workstations, use Ed25519 — shorter, faster, and more resistant to weak randomness during generation. Keep RSA-4096 for legacy hardware that doesn't understand Ed25519.
When SSH keys are not the right fit
Keys are not a universal solution for every case.
- A shared service account with no filesystem access to
~/.ssh, reachable only through a web panel — access there relies on a password or a panel token, not an SSH key. - One-off access for a contractor for 10-15 minutes — issuing a temporary password and rotating it right after is simpler than creating a key and then reliably wiping it from
authorized_keys. - A device with no secure storage for the private key, such as a shared terminal in a coworking space — a key left there compromises every server it was added to.
- A fleet of dozens of servers with no bastion host or centralized key management — when an employee leaves, someone has to manually clean
authorized_keyson every machine, and one server will almost certainly be forgotten.
Verifying that password login is actually disabled
The sshd_config file may contain a directive overridden by another file pulled in via Include, so check what the daemon actually applied, not the text of the file.
# Shows the effective sshd configuration, not the file contents
sudo sshd -T | grep -i passwordauthentication
# Explicitly request password authentication — the server must refuse
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password user@server-ip
# Confirm the last login actually used a key
sudo grep "Accepted" /var/log/auth.log | tail -5
If the first command prints passwordauthentication no, the second command fails with an access error, and the log shows an Accepted publickey line — password login is disabled for real, not just in the configuration file.