In this guide
Every student server story starts the same way: a password typed over SSH, a brute-force botnet trying thousands of logins an hour, and eventually someone's project demo going down because a weak password wasn't the fortress they thought. SSH keys replace the password with something that can't be guessed: a private key your laptop holds and never sends, and a public key the server checks against.
This guide explains how public-key login actually works — not just the commands — so you can set it up on your VPS or Raspberry Pi and fix it when it breaks.
Why keys beat passwords
A password is a shared secret you must type and the server must verify — phishable, re-usable, guessable. An SSH key pair is asymmetric:
- The private key stays on your machine. It never crosses the network — not even encrypted.
- The public key sits on every server you want to log into, inside
~/.ssh/authorized_keys.
Knowing the public key gives an attacker nothing useful: it can't be reversed into the private key, and the server never asks you to reveal the private key. Authentication is a challenge-response: the server issues a challenge tied to your public key, and only the holder of the private key can answer it. Stealing the public key is like stealing a padlock — you still can't open it.
Note: Commands below are for the standard OpenSSH client on Linux, macOS and WSL. Windows users: the same commands work in PowerShell's built-in OpenSSH or in WSL.
Step 1: generate a key pair
On your own machine:
ssh-keygen -t ed25519 -C "my-laptop"
Accept the default location (~/.ssh/id_ed25519) and — this matters — set a passphrase when asked. The passphrase encrypts the private key file, so a stolen laptop doesn't automatically mean stolen server access.
What you get:
~/.ssh/id_ed25519— the private key. Guard it like a password.~/.ssh/id_ed25519.pub— the public key. A single line starting withssh-ed25519, followed by a long encoded string and your comment. This is the only file that ever leaves your machine.
Why Ed25519? It's the modern default: short keys, fast, and strong at 256-bit-equivalent security — better than RSA-2048 in a fraction of the size. (-t rsa -b 4096 still works if some old device demands RSA.)
Step 2: install the public key on the server
The easy way:
ssh-copy-id user@server-ip
This appends your .pub contents to the server's ~/.ssh/authorized_keys and fixes permissions for you. The manual equivalent — appending the public-key line to ~/.ssh/authorized_keys on the server — works identically; ssh-copy-id just removes the fiddly bits.
Permissions are the number-one silent failure (sshd refuses to trust sloppy files):
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
If ~/.ssh or authorized_keys is group/world-writable, the server ignores the key and falls back to password — with no helpful error by default.
Step 3: log in and watch it work
ssh user@server-ip
The handshake, simplified:
- Your client offers to authenticate with a key; the server looks for the matching public key in
authorized_keys. - The server sends a challenge that only the private key can answer (a signature over a session-specific value).
- Your client signs it — prompting for the key's passphrase if you set one — and the server verifies the signature against the public key.
- The session opens. The private key never left your machine.
The first connection also asks you to verify the host key fingerprint — that's the server proving its identity to you, the mirror image of what you just did. Verify it against your provider's console before typing yes.
ssh-agent: type the passphrase once
A passphrase on every login gets old. ssh-agent holds your decrypted key in memory for the session:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
Enter the passphrase once; later logins use the agent-held key. On logout or reboot the agent forgets it — the private key file itself stays passphrase-encrypted on disk. (Most desktop Linux and macOS start an agent automatically; check with ssh-add -l.)
Using keys with Git and GitHub
Same mechanism, new authorized_keys list: paste your public key (the .pub file contents) into GitHub → Settings → SSH keys. Then clone with the SSH URL:
git clone git@github.com:username/repo.git
Push and pull then authenticate with your key instead of a password or token prompt. GitHub never sees your private key.
Troubleshooting: "Permission denied (publickey)"
Run the client verbosely and read the output — the answer is almost always in the last 20 lines:
ssh -v user@server-ip
Usual suspects, in order:
- Server-side permissions.
~/.sshmust be 700,authorized_keys600, owned by the login user. Fix via console or password login. - Wrong public key installed. The
.pubon the server must match the private key your client offers. Regenerating keys and forgetting to update the server is the classic. - Wrong username.
ssh root@...when the key sits inubuntu's authorized_keys (or vice versa). - Client offering the wrong key. With several keys, point at the right one:
ssh -i ~/.ssh/id_ed25519 user@server-ip. - Passphrase confusion. A wrong passphrase looks like a key failure; run
ssh-addand retry to isolate it. - sshd config. Some hardened servers set
PasswordAuthentication noand have no key installed — then only console or VNC access recovers the box.
Common mistakes
- Sharing the private key — emailing it to a teammate, screenshotting it, committing it to a repo. One key per person per machine; teammates generate their own and you add their public keys.
- No passphrase on a laptop key — convenient until the laptop is stolen. Passphrase plus agent is the sane middle ground.
- Pasting the private key where the public key goes — key fields on GitHub and VPS panels want the
.pubsingle line, never the private-key file (the one whose first line says BEGIN PRIVATE KEY). - One key everywhere, forever — acceptable for a student setup, but know that revoking means removing one line from every server's
authorized_keys. - Ignoring the host-key prompt — the one time "just type yes" is wrong is when the fingerprint changed unexpectedly; that can mean a man-in-the-middle or a rebuilt server. Verify out-of-band.
Quick checklist
- Key generated with Ed25519 and a passphrase
- Public key installed via
ssh-copy-id(or manual append) - Server
~/.sshis 700,authorized_keysis 600 - Host key fingerprint verified on first connect
-
ssh-agentset up so the passphrase isn't retyped constantly - Private key backed up somewhere safe (encrypted), never in a repo
FAQ
Can someone log in with just my public key? No. The public key only lets the server verify a signature — producing the signature needs the private key. That is the whole point of asymmetric authentication.
I lost my private key. Am I locked out? Of key-based login, yes — but the public key on the server is harmless without it. Log in via password (if enabled) or your provider's console, install a fresh public key, and delete the old line from authorized_keys.
Should I use the same key for GitHub and my VPS? For a student setup it's acceptable, but separate keys per purpose make revocation surgical: if the laptop is lost you remove one public key from each server instead of rotating everything.
Why does SSH still ask for a password after I set up keys? Almost always server-side permissions (700/600), the wrong key installed, or the client offering a different key than the one on the server. ssh -v tells you which.
Is ssh-copy-id safe on a shared machine? It only copies your public key. The real risk is generating keys on a machine you don't trust — generate on your own device and copy the public key over.
Do keys expire? OpenSSH keys don't expire by themselves. Rotation is manual: generate a new pair, deploy the new public key everywhere, remove the old line.
Limitations
- Keys authenticate who you are, not what you're allowed to do — server-side authorization (sudo rules, file permissions) is a separate layer.
- This guide doesn't cover certificate-based SSH (SSH CAs), hardware security keys (FIDO2 key types), or agent forwarding — all real, all beyond student-basics scope.
- If you disable password authentication entirely, keep a tested recovery path (provider console) — a broken key setup with no password fallback means lockout.
Where to go from here
- How to Deploy a MERN App on a VPS — the server you'll be SSHing into.
- HTTPS / TLS Handshake Explained — the same asymmetric ideas, protecting the web.
- GitHub Actions CI/CD Pipeline — deploy keys automating what you do by hand here.
- Git Branching Workflows for Project Teams — what the SSH-authenticated pushes feed into.
- More on the Computer / IT branch hub.