VPS Snaps

How to set up SSH key authentication and turn off passwords

Run ssh-keygen -t ed25519 on your own computer, install the public key with ssh-copy-id user@server, and prove that a key login works from a second terminal. Then put PasswordAuthentication no and KbdInteractiveAuthentication no in a file under /etc/ssh/sshd_config.d/ whose name sorts first, check it with sudo sshd -t, and run sudo systemctl reload ssh. Keep your first session open until a fresh login succeeds.

11 min readUpdated Checked against official documentation

Create a key pair

Run this on the computer you connect from, not on the server. The private key never leaves that computer; only the public half goes to servers.

Terminal
ssh-keygen -t ed25519 -C "you@laptop 2026-10"
  • -t ed25519 picks the key type. Ed25519 keys are short and fast, every OpenSSH since 6.5 (2014) accepts them, and OpenSSH 9.5 and later create them by default. Use -t rsa -b 4096 only for a system that cannot take Ed25519.
  • -C sets the comment at the end of the public key. Write who and which machine, so you can tell keys apart in a server's authorized_keys later.
  • -f ~/.ssh/name saves under another name. Without it, ssh-keygen offers ~/.ssh/id_ed25519.
Output
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/you/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/you/.ssh/id_ed25519
Your public key has been saved in /home/you/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:U7KxJ6xGLRupqQ+pTEMp3syex0rWL1VKaSekCte6xtc you@laptop 2026-10

id_ed25519 is the private key; ssh-keygen creates it readable only by you. id_ed25519.pub is the public key, one line you can share freely:

~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHk7w61eykT0Uz4vW4j1VPU8A/bC0vxXOJ2V8h8Km0Lp you@laptop 2026-10

Use a passphrase, and let ssh-agent remember it

A passphrase encrypts the private key file, so a leaked laptop backup or a stolen disk does not hand over your servers. ssh-agent holds the unlocked key in memory, so you type the passphrase once per session instead of on every connection. Keys for unattended jobs usually have none; limit those on the server instead (below). To add or change a passphrase later, run ssh-keygen -p -f ~/.ssh/id_ed25519.

Linux. Most desktop sessions already run an agent. In a plain shell, start one and add the key; -t 8h makes the agent forget it after eight hours:

Terminal
eval "$(ssh-agent -s)"
Terminal
ssh-add -t 8h ~/.ssh/id_ed25519

ssh-add -l lists the keys the agent holds. If ssh-add answers Could not open a connection to your authentication agent., no agent is running in that shell. Put AddKeysToAgent yes in ~/.ssh/config and ssh adds the key to a running agent the first time you use it.

macOS. Apple's OpenSSH can keep the passphrase in the macOS keychain:

~/.ssh/config
Host *
  AddKeysToAgent yes
  UseKeychain yes

Then store it once with ssh-add --apple-use-keychain ~/.ssh/id_ed25519 (before macOS 12 Monterey the flag was -K). UseKeychain exists only in Apple's build; if the same config file is used with another OpenSSH, add IgnoreUnknown UseKeychain above it.

Windows 10 and 11. The OpenSSH agent is a Windows service and is disabled by default. In PowerShell as administrator, set it to start automatically and start it:

PowerShell (as administrator)
Get-Service ssh-agent | Set-Service -StartupType Automatic
PowerShell (as administrator)
Start-Service ssh-agent
PowerShell
ssh-add $env:USERPROFILE\.ssh\id_ed25519

Install the public key on the server

From Linux or macOS, ssh-copy-id logs in with your password one last time and appends the key to ~/.ssh/authorized_keys for that user, creating the directory and file with safe permissions:

Terminal
ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]

-i names the key to install; without it, ssh-copy-id installs every key your agent holds. Add -p 2222 for another port. It ends with Number of key(s) added: 1.

By hand, which also works from Windows (its OpenSSH has no ssh-copy-id): print the public key with cat ~/.ssh/id_ed25519.pub (in PowerShell, Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub), log in to the server as the user who should accept it, and run these three commands:

Terminal
install -d -m 700 ~/.ssh
Terminal
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHk7w61eykT0Uz4vW4j1VPU8A/bC0vxXOJ2V8h8Km0Lp you@laptop 2026-10' >> ~/.ssh/authorized_keys
Terminal
chmod 600 ~/.ssh/authorized_keys

install -d -m 700 creates ~/.ssh if it is missing and sets it to 700 either way. Each key must stay on one line; a terminal that wraps it while pasting breaks it. To add a key for another account, become that user first (sudo -iu deploy), so the files belong to them.

sshd ignores authorized_keys when the file, ~/.ssh or the home directory can be written by anyone but the user (the StrictModes check, on by default). The server logs Authentication refused: bad ownership or modes for directory /home/deploy/.ssh and you get Permission denied (publickey). Fix it with chmod 700 ~/.ssh, chmod 600 ~/.ssh/authorized_keys and chmod go-w ~.

Test the key in a second session

Before you change anything on the server, open a new terminal and log in while refusing every method except keys:

Terminal
ssh -o PreferredAuthentications=publickey [email protected]

A shell without a server password prompt means the key works (a prompt for your key's passphrase is local and fine). Repeat for every account you need, root included if you use it, and check that sudo still works. Keep this session and the original one open until the end of the next section. If your provider has a browser console, make sure you can log in there: it does not use SSH, so it is your way back in.

Turn off password logins

On Ubuntu 22.04 and 24.04, /etc/ssh/sshd_config begins with Include /etc/ssh/sshd_config.d/*.conf. The files in that directory are read in lexical order, before the rest of the main file, and for most keywords sshd keeps the first value it reads. So the setting that counts is in the file whose name sorts first:

Terminal
sudoedit /etc/ssh/sshd_config.d/00-keys-only.conf
/etc/ssh/sshd_config.d/00-keys-only.conf
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
SettingWhat it does
PasswordAuthentication noTurns off SSH password logins.
KbdInteractiveAuthentication noTurns off keyboard-interactive logins. With UsePAM yes (Ubuntu's default), PAM can ask for the account password this way, so leaving it on can keep a password prompt alive. Ubuntu's main file already says no, but a file read earlier could say yes.
PermitRootLogin prohibit-passwordRoot may log in with a key, never with a password. It is OpenSSH's default; stating it stops another file from changing it. Use no if you only log in as a sudo user.

Why the 00- matters: on cloud images, cloud-init writes its SSH settings to /etc/ssh/sshd_config.d/50-cloud-init.conf. If the provider's setup asked for password logins (cloud-init's ssh_pwauth: true), that file says PasswordAuthentication yes, and a file named 60-something.conf, or a line at the end of sshd_config, is read later and ignored. See what each file sets:

Terminal
sudo grep -ri -E 'passwordauthentication|kbdinteractiveauthentication|permitrootlogin' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

Check the config, reload, and prove it

sshd -t checks the configuration and prints nothing when it is valid:

Terminal
sudo sshd -t

A typo stops it with a line such as /etc/ssh/sshd_config.d/00-keys-only.conf: line 1: Bad configuration option: PasswordAuthentcation. sshd -T prints the values sshd will actually use, in lowercase:

Terminal
sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin) '

Expect passwordauthentication no and kbdinteractiveauthentication no. OpenSSH before 10.1, which includes Ubuntu 22.04 (8.9) and 24.04 (9.6), prints permitrootlogin without-password: the old name for prohibit-password. Then reload:

Terminal
sudo systemctl reload ssh

Ubuntu's service is called ssh (on RHEL-family systems it is sshd). Ubuntu's unit runs sshd -t again before reloading and refuses a broken config, and open sessions survive both a reload and a restart.

Ubuntu 22.10 and later start sshd through socket activation: ssh.socket listens on port 22 and starts ssh.service when the first connection arrives. Authentication settings reload as above. Port and ListenAddress belong to the socket, so on 24.04 a change to them needs sudo systemctl daemon-reload and then sudo systemctl restart ssh.socket. Ubuntu 22.04 does not use socket activation.

Now prove it from a third terminal by asking for a password login:

Terminal
ssh -o PubkeyAuthentication=no [email protected]

It must fail at once with Permission denied (publickey)., without asking for a password. The list in brackets is what the server still accepts; (publickey,password) means passwords are still on, so look for the file that sets them.

Limit what each key can do

Each line in authorized_keys can start with options, separated by commas, with no spaces outside quotes:

OptionEffect
from="198.51.100.20"The key works only from these addresses or host names; CIDR such as 198.51.100.0/24 is allowed. A stolen copy is useless from anywhere else.
command="..."Runs this command whatever the client asked for. The client's request is in $SSH_ORIGINAL_COMMAND.
restrictTurns off port, agent and X11 forwarding, terminal allocation and ~/.ssh/rc, plus any restriction a future OpenSSH adds.
expiry-time="20261231"The key stops working after that date.

This line lets one backup server pull files from /var/www, read-only, and do nothing else. rrsync comes with rsync (/usr/bin/rrsync on Ubuntu 24.04) and allows only rsync transfers inside the directory you name. In our test with rsync 3.2.7, pulls worked, a push was refused with sending to read-only server is not allowed, and a request for /etc/ was looked up inside /var/www instead:

~/.ssh/authorized_keys
restrict,from="198.51.100.20",command="/usr/bin/rrsync -ro /var/www" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnq6CNYp0H1cWbX462YZmSVjeDHtuvEIt6GtZRPN7VJ server-b pull

Pulling backups this way keeps a compromised server away from its own copies; see protecting backups from ransomware. Give each person, computer and tool its own key, with a comment that says which: revoking one is then deleting one line. sshd logs the fingerprint of each key that logs in (Accepted publickey in journalctl -u ssh); this prints the fingerprint of every line:

Terminal
ssh-keygen -lf ~/.ssh/authorized_keys
Output
256 SHA256:U7KxJ6xGLRupqQ+pTEMp3syex0rWL1VKaSekCte6xtc you@laptop 2026-10 (ED25519)
256 SHA256:p87e8suNkBfNilX9eeNbPp/pwc8kAZArgzfbNPSU8qc vpssnaps backups (ED25519)
256 SHA256:cgBhPGRbe2aEXg2Mq2sltBCMsk+HQwUaJZSO6yT7K20 server-b pull (ED25519)

On your own computer, tell ssh which key belongs to which server:

~/.ssh/config
Host web1
  HostName 203.0.113.10
  User deploy
  IdentityFile ~/.ssh/id_ed25519
  IdentitiesOnly yes

ssh web1 then offers exactly that key. Without IdentitiesOnly yes, ssh also offers every key in your agent, and with many keys it can hit the server's limit of 6 attempts (MaxAuthTries) and fail with Too many authentication failures before reaching the right one.

Hardware security keys

With a FIDO2 security key plugged in, ssh-keygen can create a key whose private half never leaves the device:

Terminal
ssh-keygen -t ed25519-sk -C "you@laptop security key"

The file it writes is only a handle, useless without the device, and each login needs a touch. -O verify-required also demands the device PIN; -O resident stores the handle on the device so ssh-add -K can load it on another computer. The public key starts with [email protected] and goes into authorized_keys like any other. FIDO keys arrived in OpenSSH 8.2, so the server needs 8.2 or later (Ubuntu 20.04 and newer). For a security key without Ed25519 support, use -t ecdsa-sk.

Fix the common errors

Permission denied (publickey). The server accepts only keys, and none of yours matched. Check the user name (ubuntu, root and deploy each have their own authorized_keys), that the key is in that user's file, that your client offers it, and the permissions above. Watch the client:

Terminal

Offering public key: lists each key tried. Server accepts key: means it matched; Authentications that can continue: publickey right after an offer means it did not. On the server, follow the log while you try:

Terminal
sudo journalctl -u ssh -f

WARNING: UNPROTECTED PRIVATE KEY FILE! Your private key can be read by other users, so ssh refuses to use it. Any group or other permission triggers it: in our test, 0644 and 0640 failed, 0600 and 0400 worked. It often follows copying keys from a USB stick or another computer:

Output
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/you/.ssh/id_ed25519' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Terminal
chmod 600 ~/.ssh/id_ed25519

WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! The server presented a different host key from the one saved in your ~/.ssh/known_hosts, and ssh adds IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!. That is expected after you rebuild a server or reuse its IP for a new one; otherwise treat it as an attack. Before trusting the new key, log in through the provider's browser console and print the server's real fingerprint, then compare it with the one ssh showed:

Terminal
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub

If they match, remove only the old entry. Do not delete the whole known_hosts file:

Terminal
ssh-keygen -R 203.0.113.10
Output
# Host 203.0.113.10 found: line 1
/home/you/.ssh/known_hosts updated.
Original contents retained as /home/you/.ssh/known_hosts.old

On the next connection ssh says The authenticity of host ... can't be established. and asks (yes/no/[fingerprint]). Paste the fingerprint you read in the console instead of typing yes, and ssh compares the two for you.

Frequently asked questions

Is Ed25519 better than RSA for SSH keys?
For new keys, yes. Ed25519 keys are short and fast, OpenSSH has accepted them since 6.5 (2014), and OpenSSH 9.5 and later create them by default. Use RSA (-t rsa -b 4096) only for a system that cannot take Ed25519.
Should I put a passphrase on my SSH key?
Yes for keys people use: it protects the key file if your disk or backups leak, and ssh-agent means you type it once per session. Keys for unattended jobs usually have none; limit them with from=, command= and restrict in authorized_keys instead.
Why does SSH still ask for my password after I added my key?
The key is not being matched, so the server falls back to passwords: wrong user, key not in that user's authorized_keys, permissions too open, or your client is not offering it. ssh -v shows which keys are offered; sudo journalctl -u ssh on the server shows why one was refused.
How do I disable root login over SSH?
Put PermitRootLogin no in a file under /etc/ssh/sshd_config.d/ whose name sorts first, run sudo sshd -t, then sudo systemctl reload ssh. prohibit-password instead keeps root key logins and blocks root passwords.
Can I use one SSH key for several servers?
Yes, the same public key can go on any number of servers. Use separate keys for separate people and tools, so you can revoke one without touching the others.

How this was checked

Commands, limits and prices were checked against these official pages, on October 4, 2026: