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
- 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. - 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].
- The passphrase. It encrypts the private part of the key file; according to the manual, ssh-keygen uses the cipher
aes256-ctrfor 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 -pchanges it later. The option-araises the number of key derivation rounds (default 16), which slows down attempts to guess the passphrase of a stolen file [3]. - 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 -llists the loaded keys,-tsets a lifetime and-Dremoves all keys from the agent [8]. - 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 runningssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pubon the server through your provider's web console [3][4][6]. - What the server checks. sshd ignores
authorized_keysif the file, the~/.sshfolder or your home directory can be written by other users. The manual recommends that~/.sshis readable, writable and executable only by you, andauthorized_keysreadable 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-agentThen 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.comssh-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.comIf 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 noYou 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.servicesshd -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_keysnow 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 -tpasses andsshd -Tshows 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.comgreets Alex by name [12][13].
Checklist
- Look in your
.sshfolder 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
.pubfile 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 -Tbefore 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.