Create an SSH key

An SSH key replaces the password when logging into a server: the private key stays on your machine, the public one goes into authorized_keys. This guide covers why Ed25519 has been the default since OpenSSH 9.5 and DSA no longer exists as of 10.0, how to generate and transfer the key pair, which file permissions sshd insists on, and how to keep several keys apart with ~/.ssh/config.

What are SSH keys?

An SSH key is a pair of files that logs you into a server without a typed password. The server does not check whether you know a secret. It checks whether you hold one. That distinction is the whole point: a password travels to the server on every login and can be guessed, while a private key never leaves your machine.

Anyone running a server on the open internet sees what that is worth. The logs fill up with login attempts using guessed usernames and passwords. None of them get anywhere against a key, because there is nothing to guess. Turn password logins off afterwards, with PasswordAuthentication no in /etc/ssh/sshd_config, and the attempts keep coming but stop mattering.

Definition and functionality

SSH keys use asymmetric cryptography. Two files belong together: the private key, which stays on your machine, and the public key, which you place on the server. What one signs, only the other can verify.

The login itself is short. Your client tells the server which public key it wants to use. If the server finds that key in the target user’s ~/.ssh/authorized_keys, it sends back a challenge that only the matching private key can answer. The client signs, the server verifies the signature. The private key never crosses the wire.

Note the order. The public key sits on the server beforehand, not at connection time. Putting it there is the one step that takes manual work, and the step ssh-copy-id takes over further down.

Areas of application for SSH keys

SSH keys show up wherever someone reaches remote machines regularly. Administrators log into servers with them. Developers push code to GitHub, GitLab or Codeberg without typing anything on every git push. For unattended jobs there is no alternative at all: a backup script running at three in the morning cannot enter a password, and neither can a deploy step in a CI pipeline.

Some services have settled the question already. GitHub stopped accepting DSA keys (ssh-dss) on 15 March 2022, and RSA keys created after that date must use a SHA-2 signature (GitHub documentation).

Difference between public and private keys

Splitting the pair in two is the entire trick. The public key is not a secret. It can go into a ticket or an email, because the private key cannot be derived from it. The private key is the access right itself: whoever holds that file reaches every server carrying its counterpart.

“Public” still does not mean “readable by everyone”. The file ~/.ssh/authorized_keys belongs to one user, and sshd refuses the key as soon as that file, the ~/.ssh directory or the home directory is writable by others. The check is called StrictModes and ships set to yes (sshd_config(5)).

The same file holds any number of public keys, one per line. Several people or several devices can share one account that way, and any single one of them can be revoked without touching the rest.

Which key type: Ed25519, RSA, ECDSA

Older guides line up four key types as if the choice were a matter of taste. It no longer is. Of the four, two are serious options today, one is pointless and one is gone.

Ed25519 is the default. Call ssh-keygen without -t and you get an Ed25519 key, as of OpenSSH 9.5 released on 4 October 2023 (OpenSSH release notes). The key length is fixed, so -b does nothing here. The public key fits on one short line, which makes pasting it into authorized_keys noticeably easier. OpenSSH has supported the type since version 6.5 in January 2014.

RSA is the fallback for old peers. Without -b, ssh-keygen produces 3072 bits; the minimum is 1024 (ssh-keygen(1)). The weak spot is not the key but the signature scheme. OpenSSH 8.8, released 26 September 2021, switched off RSA signatures using SHA-1 because SHA-1 is considered broken. Existing RSA keys keep working as long as both ends handle RSA/SHA-256 or RSA/SHA-512.

ECDSA works, offers nothing over Ed25519 and depends on the NIST curves. There is no reason to start with it now.

DSA is gone. OpenSSH disabled DSA keys by default back in 2015, dropped them from the default build in version 9.9 on 19 September 2024, and removed the algorithm entirely in version 10.0 on 9 April 2025. Debian 13 “Trixie” ships OpenSSH 10.0 (openssh-client 1:10.0p1), so DSA does not exist there even as an option. A guide that still offers -t dsa is older than its date suggests.

Requirements for creating an SSH key

Not much is needed. Linux and macOS have everything installed already, and Windows has for several years now. The one detail that actually decides anything is the OpenSSH version on both ends of the connection.

Software and tools

On Linux and macOS, OpenSSH is part of the base system and ssh-keygen is on the path. Windows has shipped an OpenSSH client since Windows 10 build 1809; it lives in C:\Windows\System32\OpenSSH and runs in PowerShell the same way it runs in a Linux terminal (Microsoft documentation). For most people that settles the tooling question.

Two alternatives remain common on Windows:

  • PuTTY with its companion PuTTYgen, currently version 0.85 released on 16 August 2026 (download page). PuTTY stores keys in its own .ppk format, so a key destined for an OpenSSH server has to be exported from PuTTYgen into the OpenSSH format first.

  • Git Bash, which comes with Git for Windows and brings a complete OpenSSH environment along.

If the built-in client is there, neither is necessary. An extra tool solves nothing here that is not already solved.

Which OpenSSH version are you running?

The version determines which key types exist at all and which one ssh-keygen produces when you do not name one. A single command answers it:

ssh -V

Debian 12 “Bookworm” ships OpenSSH 9.2, Debian 13 “Trixie” ships 10.0. The gap is not cosmetic. Under 9.2, ssh-keygen without -t still makes an RSA key; under 10.0 it makes an Ed25519 key. DSA is absent there entirely. Ed25519 itself dates back to 2014, so only genuinely old systems on the far end force you back to RSA.

Write access to your own home directory is enough. Running ssh-keygen needs neither root nor an extra package.

Step-by-step guide to creating an SSH key

One command, two questions, done. The rest of this section explains what is being asked and why the answers matter later.

Generating the key pair

The program is called ssh-keygen and belongs to OpenSSH. One line in the terminal creates a new key:

ssh-keygen -t ed25519 -C "your_email@example.com"

-t picks the type. -C attaches a comment to the public key, usually an email address or user@host. That comment ends up at the end of the line in authorized_keys, and it is the only help you get two years later when deciding which entry is still needed. Since OpenSSH 9.5 you could drop -t ed25519 entirely, but spelling it out leaves a shell history that says what actually happened.

Only if the far end cannot handle Ed25519 does RSA come in. Then -b matters:

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

-b sets the key length in bits. Left out, it would be 3072; 4096 is the length GitHub names for older systems. On Ed25519 the option has no effect, because the length is fixed.

Next the program asks where to store the key. The default is ~/.ssh/, and the filename follows the type: id_ed25519 with id_ed25519.pub, or id_rsa with id_rsa.pub for RSA. Enter accepts the suggestion. If a key of the same type already sits there, ssh-keygen asks first, and a careless “y” overwrites it. Every counterpart on every server becomes useless at that moment, with no warning and no way back.

Selecting the storage location and passphrase

Then comes the passphrase prompt, twice. The passphrase encrypts the private key file on disk. Without one, the key is just a file that can be copied: out of any backup, out of any sync folder, by anyone who gains read access to your home directory.

Pressing Enter on an empty prompt creates a key with no passphrase. That is a decision rather than a mistake. A backup script running alone at night has no other option. Keys like that belong in authorized_keys hemmed in by restrict and command=, so they can do their one job and nothing else. For the key you work with yourself the passphrase is mandatory, and ssh-agent means typing it once per session instead of once per connection.

Two files end up in the directory: the private key id_ed25519 with no extension, and the public id_ed25519.pub. The .pub file is the one you hand out. The other never leaves the machine, not even “just briefly” in an email to yourself.

Checking the key generation

To confirm the key exists, look at the public file:

cat ~/.ssh/id_ed25519.pub

The output is one line in three parts: type, key, comment. If it starts with ssh-ed25519, the type is right. That exact line goes to the server later, complete and without a line break inserted along the way.

For the fingerprint, run ssh-keygen -l -f ~/.ssh/id_ed25519.pub. That is the value you compare against later when checking which key a server actually has on file.

Using and managing SSH keys

The key on its own does nothing. It has to reach the server, and from the second server onwards ~/.ssh/ needs some order.

Add the public key to servers

The public key has to land in ~/.ssh/authorized_keys of the account you want to log into. By hand that takes four steps. The shorter route via ssh-copy-id comes further down and ends in exactly the same place; having seen what happens, you can take the shortcut with a clear conscience.

  • Check your access: you need to be able to reach the server at all, by password or by a key that is already installed. Without existing access, only the provider’s console helps.

  • Show the public key:

cat ~/.ssh/id_ed25519.pub

Copy the whole line.

  • Log into the server:

ssh username@server_ip_address
  • Append the key: add the line to ~/.ssh/authorized_keys. Two angle brackets, not one: a single > truncates the file and discards every key already in it.

echo "your_public_key" >> ~/.ssh/authorized_keys

You can also open the file in nano or vim and paste the line by hand.

  • Set the permissions: sshd checks them and ignores the key if they are too loose. This is the most common reason a freshly installed key “just does not work”: the client falls back to asking for a password, and the server log in /var/log/auth.log mentions bad ownership or modes. The directory belongs at 700, the file at 600:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Now test it, before relying on it. Open a second session while the first one stays up. If the login fails, you are still inside and can fix it.

ssh -v username@server shows which key the client offers and whether the server takes it. The line Authentications that can continue: publickey followed by Authenticated to … using "publickey" is the proof. Only then is it worth thinking about PasswordAuthentication no.

Copy public key to the server

None of that is necessary. ssh-copy-id does the same steps in one call:

ssh-copy-id username@serveraddress

It logs in with whatever currently works, creates ~/.ssh and authorized_keys if they are missing, and appends the key. Before that it checks which of your keys are already installed and transfers only the missing ones (ssh-copy-id(1)).

Without -i it picks the most recent file matching ~/.ssh/id*.pub. With several keys around, that is guesswork. Name the one you mean:

ssh-copy-id -i ~/.ssh/id_ed25519.pub username@serveraddress

Administration of multiple SSH keys

From the second server onwards, ~/.ssh/config earns its keep. It saves typing and records which key belongs to which host. Without that mapping the client offers everything it has, one after another, and the server counts each offered key as a failed attempt. At MaxAuthTries, six by default, it drops the connection (sshd_config(5)). Carry enough keys in your agent and you get locked out of servers where you are, in fact, authorised.

  • Descriptive filenames: id_ed25519_backup says more than id_ed25519_2. The name is the only clue left later about what the key was for.

  • One entry per host: put the non-standard username and port in there next to IdentityFile instead of dragging them along on the command line every time.

Host server1
    HostName server1.example.com
    User username
    IdentityFile ~/.ssh/id_ed25519_project1
    IdentitiesOnly yes
    Port 2222
Host server2
    HostName server2.example.com
    User username
    IdentityFile ~/.ssh/id_ed25519_project2
    IdentitiesOnly yes
    Port 4321

After that, ssh server1 is enough. The IdentitiesOnly yes line is not decoration: leave it out and the client still offers everything the agent knows first, running into the same MaxAuthTries limit as before.

The other half of the work is cleaning up. A key that is no longer needed has to come out of authorized_keys, not just be deleted locally. Deleting it locally costs you the access. The entry on the server stays, and it still works for anyone who kept a copy of the file.

Security tips for dealing with SSH keys

The key is generated and installed. What remains are four places where things actually go wrong in practice.

Using strong passphrases

A passphrase is not a password for a service. It protects a file that an attacker can work through offline, at leisure. Length therefore counts for more than character variety. Four to six random words, from a password manager or a Diceware list, are easier to remember and harder to crack than “MyD0g!s$weet”. Substitutions like that have been part of every dictionary attack for years.

The file format helps too. Since OpenSSH 7.8, released 24 August 2018, ssh-keygen writes private keys in the OpenSSH format instead of OpenSSL’s PEM format, explicitly for better protection against offline password guessing. -a sets the number of derivation rounds, 16 by default (ssh-keygen(1)). A higher value makes every single guess more expensive:

ssh-keygen -t ed25519 -a 100 -C "your_email@example.com"

Keep the passphrase in a password manager. Not on a note beside the machine, and certainly not in a file beside the key.

Regular key rotation

The OpenSSH documentation recommends no fixed rotation interval. The six to twelve months often quoted are a convention rather than a measured value, and a key does not weaken from age alone. What can be said with confidence is when a key has to go immediately:

  • The device holding the private key is lost, stolen or compromised.

  • The key sat in a backup without a passphrase and somebody else had that backup.

  • Somebody leaves the team, the project or the company.

The sequence is the same in all three cases: generate the new key, install it, test it, and only then delete the old line from authorized_keys. That order is not negotiable. Delete first, discover the new key does not work, and you are looking at a server you can no longer reach.

Fingerprints tell the lines apart. ssh-keygen -lf ~/.ssh/authorized_keys prints one for every entry in the file, comment included. Comparing that against ssh-keygen -lf ~/.ssh/id_ed25519.pub shows which line is yours.

Restricting access

The private key is yours alone: chmod 600 ~/.ssh/id_ed25519. OpenSSH checks this on every connection and refuses the file with an “UNPROTECTED PRIVATE KEY FILE” warning once others can read it. On the server side sshd checks the same for ~/.ssh and authorized_keys, with StrictModes yes as the default, rejecting keys out of directories that others may write to.

The stronger lever sits in the line itself. Options may precede every key in authorized_keys. restrict switches off port forwarding, agent forwarding, X11 and PTY allocation in one word; command="…" ties the key to a single command; from="…" ties it to an origin address. For an automation key that is the real protection, since a passphrase is precisely what it cannot have.

Staying current costs nothing but attention: OpenSSH security updates arrive through the normal package manager. Which of those commands need root rights, and how to run them properly, is covered in How to use Sudo on Debian.

Back up your SSH keys

A private key without a backup is access that ends with a failed disk. A private key in the wrong backup is access for somebody else. Both problems have one answer: back it up, but only with a passphrase set, and only to the place where your other secrets live. An encrypted volume or the password manager, not the sync folder that happens to replicate to three devices.

Often the better answer is a second key. Two keys on two devices, both listed in authorized_keys, and a lost device costs one deleted line rather than the access itself. The public half needs no backup at all, since it can be recovered from the private one at any time:

ssh-keygen -y -f ~/.ssh/id_ed25519 > ~/.ssh/id_ed25519.pub

Conclusion

The short path has four steps: ssh-keygen -t ed25519, set a passphrase, ssh-copy-id, test in a second session. Everything else in this article answers the questions that come up along the way: which type, which permissions, how this scales past one server.

Three things are worth remembering. Ed25519 has been the default since OpenSSH 9.5 and DSA was removed outright in 10.0, so a guide opening with -t dsa or with RSA as the first choice is older than its date suggests. Permissions on ~/.ssh are the most common reason a key appears not to work. And the old session stays open until the new one demonstrably works.

Once access is in place, the next step is usually tightening the server: Show used ports in Linux reveals what is listening outward, and Restore iptables after a reboot keeps the firewall alive across a restart.

Further reading at the source: the ssh-keygen(1) manual page, the OpenSSH release notes for what was removed when, the GitHub documentation for the route to your own repository, and a short German guide at heise.de.