Darwvin

SSH keys done right: Ed25519 setup and login without a password

Create an Ed25519 key with ssh-keygen, install it with ssh-copy-id or by hand, fix authorized_keys permissions, load ssh-agent, then turn off password logins.

Published 13 min read

To log in without a password, create an Ed25519 key pair with ssh-keygen, put the public half in the server's ~/.ssh/authorized_keys, and protect the private half with a passphrase that ssh-agent can hold for your session. This guide covers Linux, macOS and Windows with OpenSSH. The last step turns off password logins, which is the step that locks people out when it is rushed, so keep one working session open until a fresh login with the new key succeeds.

How key login works

A key pair is two files. The private key (id_ed25519) stays on your machine, and the public key (id_ed25519.pub) is the only file you copy to servers. During login, the client signs data that includes the session identifier, and the server checks that signature against the public key it has stored. RFC 4252 defines this publickey method.

Two secrets are easy to confuse. The passphrase encrypts the private key file on your disk, so a copied file still needs the passphrase before it can sign anything. The server password, if the account has one, is a separate secret that key login does not use. With ssh-agent, you type the passphrase once per session instead of once per connection.

Ed25519 is the right default. From OpenSSH 9.5 on, ssh-keygen with no -t option creates an Ed25519 key, and older releases default to RSA. RFC 8709 defines the ssh-ed25519 public key format with a 32-byte key. As of October 2026, the newest OpenSSH release is 10.6, published on 2026-10-06 according to the OpenSSH release notes. ssh -V prints your client's version, and ssh -v also shows the server's version in its debug output.

Old appliances that do not support Ed25519 need RSA as a fallback: ssh-keygen -t rsa -b 4096. Use RSA only for hardware that cannot take Ed25519.

Step 1: Generate the key

On Linux and macOS, create ~/.ssh with the right mode, then generate the key:

mkdir -p ~/.ssh && chmod 700 ~/.ssh
ssh-keygen -t ed25519 -C "alice@laptop-2026" -f ~/.ssh/id_ed25519

The -C comment is the label you will see later in authorized_keys, so make it specific. The -f option sets the file name. ssh-keygen asks for a passphrase twice without echoing it, and a long passphrase is what protects a stolen file. The run ends with output like this, although the wording varies between releases:

Your identification has been saved in /home/alice/.ssh/id_ed25519
Your public key has been saved in /home/alice/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:... alice@laptop-2026

Print the fingerprint again at any time with ssh-keygen -lf ~/.ssh/id_ed25519.pub.

On Windows, the OpenSSH client in PowerShell does the same job, and the key lands in your profile:

New-Item -ItemType Directory -Force -Path $env:USERPROFILE\.ssh | Out-Null
ssh-keygen -t ed25519 -C "alice@win-laptop" -f $env:USERPROFILE\.ssh\id_ed25519

If ssh-keygen is missing, install the OpenSSH client from an elevated PowerShell prompt, using the capability name from Microsoft's guide:

Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

Step 2: Install the public key on the server

With ssh-copy-id

ssh-copy-id is a shell script from the portable OpenSSH distribution. It logs in with ssh, appends your key to ~/.ssh/authorized_keys on the server, and creates the file and directory if they are missing. Its man page notes that password authentication should be enabled for that first login, unless you use several identities.

ssh-copy-id -i ~/.ssh/id_ed25519.pub alice@server.example.com

Pass -i with the .pub file. Without it, ssh-copy-id uses the keys in your agent if the agent holds any, and otherwise the most recently modified ~/.ssh/id*.pub, so -i makes the choice explicit. Add -n for a dry run that prints the keys without installing them. New files and directories get restrictive modes, but an existing ~/.ssh keeps its modes, so check them afterwards.

By hand

Use the manual method when ssh-copy-id is not available. On the server:

mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo 'ssh-ed25519 AAAA...paste-the-whole-line... alice@laptop-2026' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Keep the public key on one line. A key split across two lines does not work, and some editors and terminals break long lines when you paste.

Permissions and SELinux

sshd(8) recommends that ~/.ssh be accessible only by its owner (700), and that authorized_keys be readable and writable only by its owner (600). The stricter rule is the one that bites. sshd(8) says that if authorized_keys, the ~/.ssh directory or your home directory is writable by other users, sshd will not allow the file to be used unless StrictModes is set to no. A correct key can fail with Permission denied (publickey) for exactly that reason.

Check the modes like this:

ls -ld ~ ~/.ssh
ls -l ~/.ssh/authorized_keys

Your home directory should not be writable by group or others. Fix it with chmod go-w ~ if needed. StrictModes also checks ownership, so the files must belong to the account you log into.

On Fedora and RHEL, SELinux labels matter too. The reference policy labels everything under ~/.ssh as ssh_home_t. Red Hat's SELinux guide notes that mv keeps a file's current context, so a key moved in from another directory can carry the wrong label, and it advises copying files rather than moving them. Check the labels with ls -Z ~/.ssh, and restore the defaults with:

sudo restorecon -Rv ~/.ssh

Step 3: Test with exactly that key

From the client, make ssh offer only the key you named:

ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes alice@server.example.com

IdentitiesOnly yes stops ssh from offering every key in your agent, which matters once you have more than a few. You should get a shell, with at most a passphrase prompt first. If the login fails, the guide to Permission denied (publickey) goes through the debug output line by line.

Step 4: Keep the passphrase in ssh-agent

On Linux and macOS, start an agent for your shell, load the key with a lifetime, and list what the agent holds:

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

ssh-agent creates a socket and stores its path in SSH_AUTH_SOCK. The -s flag prints shell commands that set that variable, so eval applies them to your shell. The -t option of ssh-add sets how long the agent keeps the key. Before you start a second agent, check echo $SSH_AUTH_SOCK, because some desktop sessions already run one.

To load keys on first use instead, add this to ~/.ssh/config:

Host *
    AddKeysToAgent yes
    IdentityFile ~/.ssh/id_ed25519

With AddKeysToAgent yes, ssh adds the key and its passphrase to the agent the first time it is used, with the default lifetime. The SSH config guide explains the other keywords. If you keep your servers in that config file, the RemoteOpsX desktop app (Linux, with Windows in beta) can import it. Review the imported list before you connect.

On macOS, Apple's build of ssh can store the passphrase in the Keychain. The GitHub documentation uses this pair of settings:

ssh-add --apple-use-keychain ~/.ssh/id_ed25519
Host *
    AddKeysToAgent yes
    UseKeychain yes
    IdentityFile ~/.ssh/id_ed25519

UseKeychain is an Apple keyword, and upstream OpenSSH does not document it. An upstream build rejects it with Bad configuration option: usekeychain. On such a build, put IgnoreUnknown UseKeychain above the UseKeychain line, because ssh_config(5) does not apply IgnoreUnknown to unknown options that appear before it.

On Windows, the OpenSSH agent is a service, and it is disabled by default. From an elevated PowerShell prompt, enable it and load the key:

Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519

Microsoft's guide says the agent cannot return the private key when a strong algorithm is used, so back up the key before you delete the local copy.

Windows as the SSH server

If the Windows machine runs the OpenSSH server, the key location depends on the account type. A standard user's key goes in C:\Users\NAME\.ssh\authorized_keys. Administrator accounts must use C:\ProgramData\ssh\administrators_authorized_keys instead, and that file's ACL must allow only Administrators and SYSTEM:

icacls.exe "$env:ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"

On non-English Windows the group names differ. Microsoft's guide then uses the SID of the Administrators group, *S-1-5-32-544, in place of the name:

icacls.exe "$env:ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "*S-1-5-32-544:F" /grant "SYSTEM:F"

Hardware keys, briefly

A FIDO2 security key keeps the signing key on the device. The key file on disk holds a key handle, but the per-device private key cannot be exported from the authenticator, and ssh-keygen(1) describes that split. Generate a key with the ed25519-sk type:

ssh-keygen -t ed25519-sk -C "alice-securitykey" -f ~/.ssh/id_ed25519_sk

Both the client and the server need OpenSSH 8.2 or newer, the release that added ecdsa-sk and ed25519-sk in February 2020. Each signature needs a touch by default. -O no-touch-required removes that at generation time, but sshd refuses such signatures unless an authorized_keys option allows them, such as no-touch-required on that key's line.

Resident keys (-O resident) store the key handle on the authenticator, which makes them easier to use on several computers. They typically need a PIN set on the device first. ssh-keygen(1) also warns that storing both parts of a key on the authenticator makes a stolen device more useful to an attacker. Register a second security key or keep a backup key elsewhere, because losing the only one locks you out.

Turn off password logins safely

Confirm that key login works from a second terminal before you change anything, and keep your first session open until a new login succeeds with the new settings. Put these lines in /etc/ssh/sshd_config, or in a .conf file in /etc/ssh/sshd_config.d/ if your main file includes that directory:

PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password

Turn off both password switches. With UsePAM yes, PAM keyboard-interactive authentication usually serves the same role as a password, and sshd_config(5) says to disable one of the two. Turning off only PasswordAuthentication can leave a password prompt, so disable KbdInteractiveAuthentication as well. Use PermitRootLogin no if you never log in as root over SSH.

Drop-ins can override your edit. sshd uses the first value it reads for each keyword (sshd_config(5)), and an Include line decides when a drop-in is read. Debian's packaged sshd_config has Include /etc/ssh/sshd_config.d/*.conf above its keyword lines, so drop-ins there come before the rest of the file. A drop-in that an image or provisioning tool installed can set PasswordAuthentication yes, and that value wins over the line you added. Find the Include line with grep -n Include /etc/ssh/sshd_config, then check the effective values. sshd -T prints them after Include processing:

sudo sshd -T | grep -Ei '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication) '

You want pubkeyauthentication yes, passwordauthentication no and kbdinteractiveauthentication no. Then check the syntax and reload. The unit is named ssh on Debian and Ubuntu, and sshd on Fedora and RHEL:

sudo sshd -t && sudo systemctl reload ssh   # use sshd on Fedora and RHEL

sshd -t checks the configuration and the host keys, but it does not print the effective values, so keep the sshd -T check too. From a new terminal, force a password attempt:

ssh -o PubkeyAuthentication=no alice@server.example.com

It should fail straight away with Permission denied (publickey). and no password prompt. Then confirm that key login still works from a third terminal before you close the first.

Rotate and retire keys

Rotation repeats setup, then removes the old line. Create the new key, install it while the old key still works, and test the new key on its own:

ssh-keygen -t ed25519 -C "alice@laptop-rotated" -f ~/.ssh/id_ed25519_new
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub alice@server.example.com
ssh -i ~/.ssh/id_ed25519_new -o IdentitiesOnly=yes alice@server.example.com

Then, on the server, find the old line by its comment, back up the file, and delete that line in an editor:

grep -n "alice@laptop-2026" ~/.ssh/authorized_keys

Rotate when a device is lost or retired, when someone leaves, or when you suspect a key leaked. For a lost laptop, the urgent step is removing its public key from every server, because the private key and its passphrase keep working until that line is gone. If you lose a .pub file, ssh-keygen -y -f ~/.ssh/id_ed25519 prints the public key from the private key, but nothing can rebuild a lost private key. To change only the passphrase, run ssh-keygen -p -f ~/.ssh/id_ed25519. The public key does not change, so authorized_keys stays as it is.

When it still fails

SymptomLikely causeFix
Password prompt still appears after you disabled passwordsKbdInteractiveAuthentication is still yes, or a drop-in sets itCheck sshd -T, fix the drop-in, reload
Permission denied (publickey) with a correct keyModes on ~/.ssh or authorized_keys, or a writable home directorySet 700 and 600, fix the home directory
Passphrase asked on every connectionKey not loaded into ssh-agentAdd AddKeysToAgent yes and run ssh-add
ssh-add -l reports no identitiesKey loaded into a different agentCompare SSH_AUTH_SOCK in the same shell
Windows admin account ignores the keyKey placed in the per-user fileUse administrators_authorized_keys

FAQ

Should I use Ed25519 or RSA?

Use Ed25519 unless a device cannot accept it. It is the default for ssh-keygen from OpenSSH 9.5 on, and RFC 8709 standardizes the ssh-ed25519 format. Use RSA with -b 4096 only for old hardware.

Can I skip the passphrase?

You can, but then anyone who copies the private key file can log in as you. A passphrase costs one prompt per session when ssh-agent holds the key, so the trade-off is small.

Why does ssh-copy-id not work on Windows?

It is a /bin/sh script, and plain Windows has no POSIX shell. On Windows, paste the public key by hand, or run ssh-copy-id from WSL.

What if I lose my laptop?

Remove its public key from every authorized_keys file, using the comment to find the line. Until you do, anyone with the private key file and its passphrase can log in as you.

Why does ssh still ask for my passphrase after ssh-add?

Run ssh-add -l in the same shell you use for ssh. If it prints "The agent has no identities.", the key went into a different agent. Compare the socket path with echo $SSH_AUTH_SOCK.

References

Get product news by email

We use your address only for these emails. The unsubscribe link is in every email. Read our privacy policy.