Skip to content
Bytearium

No ads or affiliate links active

Cloud & development

SSH keys instead of passwords: a clean setup

Generate an SSH key on Windows, macOS or Linux: Ed25519 with a passphrase, ssh-agent, copy it to the server, test it, then switch off password login.

A server that accepts SSH passwords from the internet is a front door that anyone can try, as often as they like. A key pair swaps that door for a lock that only your key fits. The one catch: you must not lose the key.

The short answer

How do you generate an SSH key on Windows, macOS or Linux and log in without a password? Run ssh-keygen -t ed25519 and protect the key with a passphrase [1][2][3]. Copy only the public half, the .pub file, into ~/.ssh/authorized_keys on the server, for example with ssh-copy-id [4][9]. Test the key login in a second window. Only when it works, set PasswordAuthentication no and KbdInteractiveAuthentication no, check the configuration with sshd -t and restart the SSH service [4][5][14]. Then back up the key, or better, register a second one. All commands follow the OpenSSH manuals, Microsoft Learn, GitHub Docs and Ubuntu's server documentation as of 25 Oct 2026. We did not run them on a live server for this guide.

How it works

  1. Two halves of one key. ssh-keygen creates a private key, for example ~/.ssh/id_ed25519, and a public key with the same name plus .pub. The private file should not be readable by anyone but you; the public one needs no secrecy [3]. The server keeps the public keys it accepts in ~/.ssh/authorized_keys, one key per line [4]. In the words of the ssh manual: the server knows the public key, and only you know the private key [6]. Our lexicon entry on public-key cryptography explains the idea behind it.
  2. Why Ed25519. According to the OpenSSH manual, Ed25519 is the default key type of ssh-keygen [3]. Microsoft's OpenSSH for Windows also uses Ed25519 when you name no algorithm [1]. GitHub's guide uses it too and switches to RSA with 4,096 bits only for legacy systems without Ed25519 support [2]. Ubuntu's server documentation prefers Ed25519 over RSA for its shorter keys and lower computing needs [14].
  3. The passphrase. It encrypts the private part of the key file; according to the manual, ssh-keygen uses the cipher aes256-ctr for this by default [3]. Without one, anyone who copies the file can log in as you. Microsoft advises against an empty passphrase, and GitHub recommends setting one [1][2]. ssh-keygen -p changes it later. The option -a raises the number of key derivation rounds (default 16), which slows down attempts to guess the passphrase of a stolen file [3].
  4. The agent. So that you don't type the passphrase for every connection, ssh-agent keeps the unlocked key in memory, and ssh-add hands it over. ssh-add -l lists the loaded keys, -t sets a lifetime and -D removes all keys from the agent [8].
  5. The server's fingerprint. On your first connection, ssh shows the fingerprint of the server's host key and then stores the key in ~/.ssh/known_hosts. If it ever changes, ssh warns you and disables password authentication, so that a fake server cannot catch your password [6]. Compare the fingerprint with one you got another way, for example by running ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub on the server through your provider's web console [3][4][6].
  6. What the server checks. sshd ignores authorized_keys if the file, the ~/.ssh folder or your home directory can be written by other users. The manual recommends that ~/.ssh is readable, writable and executable only by you, and authorized_keys readable and writable only by you [4]. The option StrictModes controls this check [5].

Step 1: create the key

The command is the same in PowerShell on Windows, in Terminal on macOS and in a Linux shell:

ssh-keygen -t ed25519 -C "laptop-2026"

Press Enter to accept the default location, then type a passphrase twice [2]. If a key with that name already exists, GitHub recommends a custom file name instead of overwriting it [2]. The option -C sets a comment [3]. GitHub's example puts an email address there [2]; we prefer the device name, so that you can later tell which line in authorized_keys belongs to which device. ssh-keygen -l -f ~/.ssh/id_ed25519.pub shows your key's fingerprint [3].

On Windows you need the OpenSSH client. Microsoft says the latest builds of Windows 10 and Windows 11 include a built-in SSH client [11]. If ssh-keygen is missing, search the Start menu for Optional features, select Add a feature, pick OpenSSH Client and install it. Microsoft names Windows 10 build 1809 as the minimum [10]. The keys end up in C:\Users\<name>\.ssh\ [1].

Hardware security keys are an option, not a must. With one plugged in (the manual calls it a FIDO authenticator), ssh-keygen -t ed25519-sk creates a key whose private part stays on the device. The file on your disk only holds a key handle, and the device has to be attached and usually touched whenever you log in [2][3]. If the device reports "invalid format" or "feature not supported", GitHub suggests -t ecdsa-sk instead [2]. Two options are worth knowing: -O resident stores the handle on the device itself, and -O verify-required asks for user verification for every signature [3]. We have not checked which versions of the Windows client or of your server support these key types, so test them before you rely on them. Our lexicon entry on passkeys explains FIDO2 sign-in.

Step 2: load the key into the agent

Windows. The ssh-agent service is disabled by default. Run these two lines in PowerShell as administrator [1]:

Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent

Then add the key in a normal PowerShell window with ssh-add $env:USERPROFILE\.ssh\id_ed25519 [1][2]. GitHub's guide sets the startup type to Manual instead of Automatic [2]. Microsoft also recommends backing up the private key to a secure location; more on that in step 6 [1].

macOS. Start the agent with eval "$(ssh-agent -s)". In ~/.ssh/config, add a Host block, which GitHub's example writes for github.com, with the lines AddKeysToAgent yes, UseKeychain yes and IdentityFile ~/.ssh/id_ed25519. Then run ssh-add --apple-use-keychain ~/.ssh/id_ed25519, and the passphrase lives in the macOS keychain [2].

Linux. Start the agent with eval "$(ssh-agent -s)" and add the key with ssh-add ~/.ssh/id_ed25519 [2].

Step 3: put the public key on the server

With ssh-copy-id. On Linux, and wherever the tool is installed, one command does the job:

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

ssh-copy-id logs in once with your password, appends the key to ~/.ssh/authorized_keys on the server and creates the file and folder if needed. It first checks whether the key is already installed and skips it if so. -n only shows what it would do, and -p sets another port [9]. Ubuntu's documentation uses the same tool [14].

By hand, for example from Windows. Copy the public key to the clipboard with cat ~/.ssh/id_ed25519.pub | clip in PowerShell [12]. Log in to the server with your password and add the key as a new line to ~/.ssh/authorized_keys with an editor: one key per line, without line breaks inside the key [4][12]. If the ~/.ssh folder doesn't exist yet, create it and give only yourself access, as described above [4]. Then make sure that only you can write to the file. Ubuntu's documentation uses chmod go-w .ssh/authorized_keys for this, run in your home directory [14].

In the provider panel. If your VPS provider lets you add an SSH key when you create a server, paste the content of the .pub file there.

On GitHub. Open Settings, then SSH and GPG keys, select New SSH key, choose Authentication Key as the key type, paste the public key and select Add SSH key [12]. Then run ssh -T git@github.com, compare the fingerprint with the ones GitHub publishes, and expect the message "Hi USERNAME! You've successfully authenticated, but GitHub does not provide shell access." [13]

On a Windows server. For a standard user, the key goes into C:\Users\username\.ssh\authorized_keys. For members of the Administrators group it goes into C:\ProgramData\ssh\administrators_authorized_keys instead, and only Administrators and SYSTEM may have access to that file. Microsoft Learn shows the exact commands, including the access rights [1].

Step 4: test before you lock the door

Keep your current session open. In a second window, connect in a way that rules out passwords on the client side [6][7]:

ssh -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no alex@server.example.com

If you get in and are asked for nothing but your key's passphrase, the key works. If not, ssh -v shows each step of the connection [6], and on Ubuntu sudo journalctl -fu ssh.service shows the server's side [14]. The usual suspects are permissions that are too open [4], a key pasted with a line break [12] and, on Windows servers, the separate file for administrators [1].

Step 5: switch off password login

On Ubuntu, configuration snippets in /etc/ssh/sshd_config.d/ take precedence over the main file, because OpenSSH uses the first value it reads for most options [14]. The manual adds that included files are read in lexical order [5]. So a file whose name sorts early wins, for example /etc/ssh/sshd_config.d/00-keys-only.conf with these two lines:

PasswordAuthentication no
KbdInteractiveAuthentication no

You need both: with PAM in use, the manual advises disabling either PasswordAuthentication or KbdInteractiveAuthentication, and switching off both covers both paths [5]. By default, root cannot log in with a password anyway, because PermitRootLogin defaults to prohibit-password [5]; check the value in the output of sshd -T all the same. Our Docker VPS guide goes one step further and sets it to no. Now check and apply the change:

sudo sshd -t
sudo sshd -T | grep -iE "passwordauthentication|kbdinteractiveauthentication|permitrootlogin"
sudo systemctl restart ssh.service

sshd -t only checks the configuration and the keys; sshd -T also prints the configuration that is actually in effect [4]. Both password values have to read no. The restart command comes from Ubuntu's documentation; on other distributions, follow their documentation [14]. Keep the old session open. Then test from a third window: ssh -o PubkeyAuthentication=no alex@server.example.com must now be refused, and the key login from step 4 must still work [7].

Step 6: back up the key and plan for its loss

Microsoft puts it plainly: a private key is the equivalent of a password and needs the same protection. If you lose it, you have to create a new key pair and update the public key on every system you use [1]. Microsoft recommends backing up the private key to a secure location [1]. Our assessment: a second key is the more robust plan. Create one on a second device or on a hardware security key and add both public keys to authorized_keys and to GitHub. Then a lost laptop is an annoyance, not a lockout. When a device goes missing, delete its line from authorized_keys and its entry on GitHub. Keep any copy of a private key encrypted, for example in your password manager, and only ever paste the .pub file into forms [12].

A worked example (names are placeholders)

The setup is invented and shows a plan, not a test. Alex has a Windows 11 laptop and a Linux desktop and rents a VPS that answers as server.example.com.

  • Both devices get their own key with ssh-keygen -t ed25519, with the comments laptop-2026 and desktop-2026 and two different passphrases [3].
  • On the laptop, the ssh-agent service starts automatically and holds the key [1].
  • From the desktop, ssh-copy-id installs the first key [9]. The laptop's key goes in by clipboard and editor as a second line [12]. authorized_keys now has exactly two lines, one per device [4].
  • Both devices log in with the test command from step 4.
  • On the server, the snippet from step 5 goes in, sshd -t passes and sshd -T shows no for both password values; then the service restarts. A password login from a third window is refused.
  • On GitHub, both public keys are added as authentication keys, and ssh -T git@github.com greets Alex by name [12][13].

Checklist

  • Look in your .ssh folder for existing keys before you create a new one, so that you don't overwrite one.
  • Create an Ed25519 key with a passphrase and a comment that names the device.
  • Load the key into ssh-agent so that you type the passphrase once per session.
  • Copy only the .pub file to the server, to GitHub or into the provider panel.
  • Compare the server's fingerprint on the first connection instead of typing yes blindly.
  • Test the key login in a second window with password methods switched off on the client side.
  • Switch off PasswordAuthentication and KbdInteractiveAuthentication only after that test passes.
  • Check the effective configuration with sshd -T before you restart the service.
  • Keep one session open until the new login works.
  • Register a second key and write down how you would remove a lost one.

Related pages: Docker on a VPS: firewall and hardening; Running a Minecraft server on a VPS; Palworld, Valheim and co. on a VPS; WireGuard or Tailscale for reaching your homelab; Passkey (FIDO2 sign-in); Public-key cryptography.

Primary sources

These sources are the basis of this page. That does not mean every claim was checked individually; whether a review record exists is shown above under “Status”. The interpretation and checklist are our own work.

There is no documented review record for this version yet. The text relies on the sources listed.

How this page was made: Bytearium creates these pages with the help of AI and is responsible for publishing them. AI can be wrong, so we show our sources and review status openly.

  1. Microsoft LearnKey-based authentication in OpenSSH for Windows
  2. GitHub DocsGenerating a new SSH key and adding it to the ssh-agent
  3. OpenSSH (man7.org)ssh-keygen(1)
  4. OpenSSH (man7.org)sshd(8)
  5. OpenSSH (Arch Linux manual pages)sshd_config(5)
  6. OpenSSH (man7.org)ssh(1)
  7. OpenSSH (man7.org)ssh_config(5)
  8. OpenSSH (man7.org)ssh-add(1)
  9. OpenSSH (man7.org)ssh-copy-id(1)
  10. Microsoft LearnGet started with OpenSSH for Windows
  11. Microsoft LearnTutorial: SSH in Windows Terminal
  12. GitHub DocsAdding a new SSH key to your GitHub account
  13. GitHub DocsTesting your SSH connection
  14. CanonicalOpenSSH server

This page has no ads and no affiliate links.