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.
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.
ssh-keygen -t ed25519 -C "you@laptop 2026-10"-t ed25519picks 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 4096only for a system that cannot take Ed25519.-Csets the comment at the end of the public key. Write who and which machine, so you can tell keys apart in a server'sauthorized_keyslater.-f ~/.ssh/namesaves under another name. Without it, ssh-keygen offers~/.ssh/id_ed25519.
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-10id_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-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHk7w61eykT0Uz4vW4j1VPU8A/bC0vxXOJ2V8h8Km0Lp you@laptop 2026-10Use 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:
eval "$(ssh-agent -s)"ssh-add -t 8h ~/.ssh/id_ed25519ssh-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:
Host *
AddKeysToAgent yes
UseKeychain yesThen 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:
Get-Service ssh-agent | Set-Service -StartupType AutomaticStart-Service ssh-agentssh-add $env:USERPROFILE\.ssh\id_ed25519Install 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:
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:
install -d -m 700 ~/.sshecho 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHk7w61eykT0Uz4vW4j1VPU8A/bC0vxXOJ2V8h8Km0Lp you@laptop 2026-10' >> ~/.ssh/authorized_keyschmod 600 ~/.ssh/authorized_keysinstall -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:
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:
sudoedit /etc/ssh/sshd_config.d/00-keys-only.confPasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password| Setting | What it does |
|---|---|
PasswordAuthentication no | Turns off SSH password logins. |
KbdInteractiveAuthentication no | Turns 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-password | Root 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:
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:
sudo sshd -tA 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:
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:
sudo systemctl reload sshUbuntu'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:
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:
| Option | Effect |
|---|---|
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. |
restrict | Turns 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:
restrict,from="198.51.100.20",command="/usr/bin/rrsync -ro /var/www" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnq6CNYp0H1cWbX462YZmSVjeDHtuvEIt6GtZRPN7VJ server-b pullPulling 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:
ssh-keygen -lf ~/.ssh/authorized_keys256 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:
Host web1
HostName 203.0.113.10
User deploy
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yesssh 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:
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:
ssh -v [email protected]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:
sudo journalctl -u ssh -fWARNING: 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:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ 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.chmod 600 ~/.ssh/id_ed25519WARNING: 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:
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pubIf they match, remove only the old entry. Do not delete the whole known_hosts file:
ssh-keygen -R 203.0.113.10# Host 203.0.113.10 found: line 1
/home/you/.ssh/known_hosts updated.
Original contents retained as /home/you/.ssh/known_hosts.oldOn 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=andrestrictin 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 -vshows which keys are offered;sudo journalctl -u sshon the server shows why one was refused. - How do I disable root login over SSH?
- Put
PermitRootLogin noin a file under /etc/ssh/sshd_config.d/ whose name sorts first, runsudo sshd -t, thensudo systemctl reload ssh.prohibit-passwordinstead 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:
- OpenSSH ssh-keygen(1) manual
- OpenSSH ssh-agent(1) manual
- OpenSSH ssh-add(1) manual
- OpenSSH ssh(1) manual
- OpenSSH ssh_config(5) manual
- OpenSSH sshd(8) manual: AUTHORIZED_KEYS FILE FORMAT
- OpenSSH sshd_config(5) manual
- sshd_config(5) manual (Ubuntu 24.04)
- sshd_config(5) manual (Ubuntu 22.04)
- ssh-copy-id(1) manual (Ubuntu 24.04)
- rrsync(1) manual (Ubuntu 24.04)
- OpenSSH release notes
- Launchpad: openssh in Ubuntu 20.04
- Ubuntu Server documentation: OpenSSH server
- Ubuntu Discourse: SSHd now uses socket-based activation (Ubuntu 22.10 and later)
- cloud-init documentation: Set Passwords module
- cloud-init source: ssh_util.py
- Apple Technical Note TN2449: OpenSSH updates in macOS
- GitHub Docs: Generating a new SSH key and adding it to the ssh-agent
- Microsoft Learn: Key-based authentication in OpenSSH for Windows