Password authentication on SSH is the single most attacked login surface on the internet. Bots hammer port 22 with thousands of password guesses every hour, and even a strong password can fall to credential stuffing or a simple misconfiguration. SSH key authentication fixes this at the root: instead of something you know, you log in with something you have — a cryptographic key pair that no bot can brute-force.

In this guide you will generate an SSH key pair on your local machine, install the public key on your Ubuntu server, verify that key login works, and then disable password authentication entirely. By the end, your server will refuse every password login attempt, and only your key can get in. The whole process takes about 15 minutes.

Prerequisites

  • An Ubuntu 24.04 or 22.04 server you can already reach with ssh user@your-server (password login still enabled for now).
  • A sudo-capable user on that server. You do not need root.
  • Your local computer: Linux, macOS, or Windows 11 with OpenSSH installed (Windows 11 ships it by default — check with ssh in PowerShell).
  • A second SSH session kept open during the final steps, so you can never lock yourself out.

Step 1 — Generate your SSH key pair locally

Run this on your local machine, not on the server. It creates a public key (safe to share) and a private key (never leaves your machine):

ssh-keygen -t ed25519 -C "your-email@example.com"

The -t ed25519 flag selects the Ed25519 algorithm — short keys, fast signatures, and the current best practice. The -C comment is just a label so you can tell keys apart later. Accept the default file location (~/.ssh/id_ed25519), and set a passphrase when prompted. A passphrase means that even if someone steals your laptop, they cannot use your key without it.

On very old SSH clients that do not understand Ed25519 (rare in 2026), fall back to RSA instead:

ssh-keygen -t rsa -b 4096 -C "your-email@example.com"

Step 2 — Copy your public key to the server

The easiest way is ssh-copy-id, which installs your public key into the server's ~/.ssh/authorized_keys and fixes permissions for you:

ssh-copy-id user@your-server-ip

Replace user with your server username and your-server-ip with the server's IP or hostname. You will be asked for your password one last time — that is normal, this is the final password login you will ever make to this server.

On Windows 11, ssh-copy-id is not included. Do it manually instead — this PowerShell one-liner appends your public key to the server:

type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh user@your-server-ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

This creates the .ssh directory with the correct permissions, appends your public key to authorized_keys, and locks that file down. SSH refuses to trust keys if these permissions are wrong — 700 on the directory and 600 on the file are required.

Step 3 — Verify that key login works

Open a new terminal and connect:

ssh user@your-server-ip

If you set a passphrase, you will be prompted for it — not the server password. If you log in without any password prompt at all, your SSH agent has the key loaded, which is also fine.

To confirm the server really used your key and not a cached password, run:

ssh -v user@your-server-ip 2>&1 | grep "Server accepts key"

The -v flag makes SSH print its authentication negotiation. Seeing Server accepts key: ... ED25519 proves the key was used. If it still asks for your server password here, stop — go to the troubleshooting section below and fix it before continuing.

Step 4 — Lock down the SSH server config

Now that key login is proven, disable password authentication. On the server, edit the SSH daemon config:

sudo nano /etc/ssh/sshd_config

Set (or add) these three directives:

PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password

PasswordAuthentication no tells the server to refuse all password logins, including keyboard-interactive fallbacks. PubkeyAuthentication yes makes key login explicit (it is the default, but spelling it out protects you from config includes that might disable it). PermitRootLogin prohibit-password keeps root reachable by key only — root with a password is never needed.

Before saving, validate the config syntax:

sudo sshd -t

sshd -t parses the configuration and prints errors without touching the running daemon. Silence means the config is valid. Then reload the daemon so the change takes effect:

sudo systemctl reload ssh

A reload re-reads the config without dropping existing connections — your current session stays alive, which is exactly what you want while testing.

Step 5 — Prove password login is dead

From a new local terminal, try to force password authentication:

ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@your-server-ip

You should get Permission denied (publickey) immediately. That error is the whole point — the server no longer even attempts password authentication. Your normal key-based ssh user@your-server-ip should still connect instantly.

On the server, confirm the daemon accepted the new config and watch the logs for rejected attempts:

sudo systemctl status ssh --no-pager | head -5
sudo tail -20 /var/log/auth.log | grep sshd

You will see lines like Failed password for ... Connection closed by authenticating user followed by disconnects — that is bots discovering passwords no longer work. Your key logins appear as Accepted publickey for user ....

Verify it worked

  • ssh user@your-server-ip connects with your key (passphrase only).
  • ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@your-server-ip fails with Permission denied (publickey).
  • sudo sshd -T | grep -i "passwordauthentication" prints passwordauthentication no — the effective running config.
  • sudo grep PasswordAuthentication /etc/ssh/sshd_config shows your edit is the active one (watch out for commented #PasswordAuthentication yes lines — SSH uses the first occurrence it finds, so make sure yours comes first or the commented lines stay commented).

Common issues & fixes

"Permission denied (publickey)" when the key should work

Nine times out of ten this is file permissions. On the server, run:

chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
sudo chown -R $USER:$USER ~/.ssh

SSH silently ignores authorized_keys if it is group-writable or owned by someone else. Also check the server logs: sudo tail /var/log/auth.log | grep sshd will say exactly why a key was rejected.

You locked yourself out

Do not panic. Every major VPS provider (Hetzner, DigitalOcean, AWS, Linode) has a web-based console or recovery access. Log in through the provider's console, re-enable passwords temporarily (PasswordAuthentication yes + sudo systemctl reload ssh), and redo Step 2. This is why Step 3 exists — never disable passwords until key login is proven.

Windows: "ssh-copy-id is not recognized"

Expected — use the PowerShell one-liner from Step 2. Also confirm OpenSSH client is installed: in PowerShell run Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH.Client*' and add it via Settings → Apps → Optional features if missing.

The server still asks for a password after your edit

Two usual causes. First, the daemon was not reloaded — run sudo systemctl reload ssh again. Second, a later config file overrides yours: Ubuntu loads /etc/ssh/sshd_config.d/*.conf before sshd_config, and the first value wins — check with sudo sshd -T | grep -i passwordauth to see the effective value, and edit the .d file instead if needed.

Forgotten key passphrase

You cannot recover a passphrase, but you can change it if you still have access to the key file: ssh-keygen -p -f ~/.ssh/id_ed25519. If you lost the private key entirely, log in with your existing access, remove the old public key from ~/.ssh/authorized_keys, and start again at Step 1.

Conclusion

You now have a server that only answers to your cryptographic key: Ed25519 key pair generated, public key installed, password authentication disabled, root password login prohibited, and both directions verified. The background noise of brute-force bots becomes harmless — they can guess passwords forever and never get in.

For the next layer, consider Fail2Ban to silence the bot noise entirely, and SSH agent forwarding alternatives like ProxyJump for jumping between servers. If you would rather have a professional handle your server hardening, XpertBees sets up and secures Ubuntu servers for clients every day — reach out and we will lock yours down properly.