How to add an SSH key to a server

A server trusts a key listed in the account's ~/.ssh/authorized_keys, one public key per line. Put it there, get the permissions right, turn off passwords, and fix Permission denied.

updated

Quick answer. Run ssh-copy-id with your public key and the remote account. It asks for the password once and adds the key to ~/.ssh/authorized_keys on the server. Then log in without a password.

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
ssh user@host

How key login works

When you connect, the client tells the server which public key it would like to use. sshd looks the key up in the target account's ~/.ssh/authorized_keys. If it is listed, the server asks the client to sign a value that is unique to this session, and checks the signature with the public key. The private key never leaves your computer, and a recorded login cannot be replayed.

So adding a key means adding one line, the content of your .pub file, to that file on the server, with permissions sshd accepts. Everything else on this page is either a convenient way to do that or a reason it can fail. If you still need a key, the Linux, macOS and Windows guides cover creating one.

Add an SSH key to a server, step by step

  1. Have a key pair ready

    You need the public key, the .pub file. If you do not have one yet, run ssh-keygen -t ed25519 or generate one in your browser with the sshkeygen.dev generator.

  2. Copy the key with ssh-copy-id

    From your computer, run ssh-copy-id with the public key and the account you want to log in as. It asks for that account's password one last time.

    ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host

    For a non-standard port add -p 2222. ssh-copy-id skips keys that are already installed, so running it twice is harmless.

  3. Or append it by hand

    Without ssh-copy-id (on Windows, for example), pipe the key over an ordinary SSH session. This creates the folder and file if needed and sets their permissions:

    cat ~/.ssh/id_ed25519.pub | ssh user@host "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
  4. Log in with the key

    Connect again. You should be asked for the key's passphrase, or nothing if ssh-agent holds it, but not for the account password.

    ssh -i ~/.ssh/id_ed25519 user@host
  5. Turn off password logins

    Once key login works for every account you need, and with a second session still open as a safety net, set these in /etc/ssh/sshd_config and reload the SSH service:

    PubkeyAuthentication yes
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    PermitRootLogin prohibit-password

    Check the file with sudo sshd -t, then reload with sudo systemctl reload ssh on Debian and Ubuntu or sudo systemctl reload sshd on Fedora, RHEL and Arch.

What ssh-copy-id does

ssh-copy-id ships with OpenSSH on Linux and macOS. It logs in with whatever works (usually the password), creates ~/.ssh and authorized_keys if needed, appends the keys that are not there yet and fixes their permissions. A typical run:

$ ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/you/.ssh/id_ed25519.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
[email protected]'s password:

Number of key(s) added: 1

Now try logging into the machine, with: "ssh -i /home/you/.ssh/id_ed25519 '[email protected]'"
and check to make sure that only the key(s) you wanted were added.

Always pass -i with the .pub file. Without it, ssh-copy-id installs every key loaded in your agent, or else the newest key in ~/.ssh, which may not be the one you meant. -n shows what would be installed without changing anything.

Add a key for another user

As an administrator you often install a colleague's public key for them. Do it as that user so the files get the right owner:

sudo -u bob mkdir -p -m 700 /home/bob/.ssh
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... bob@laptop' | sudo -u bob tee -a /home/bob/.ssh/authorized_keys
sudo chmod 600 /home/bob/.ssh/authorized_keys

Ask for the .pub file only, and confirm its fingerprint with the person through a second channel, for example by reading ssh-keygen -lf bob.pub to each other. The how and why is in SSH key fingerprints.

Inside authorized_keys

A typical file holds one key per line, with optional restrictions at the start of a line:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC0... you@laptop
from="203.0.113.0/24" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHk... office
restrict,command="/usr/local/bin/backup" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM2... backup-job
  • from="..." accepts the key only from the listed addresses or networks.
  • command="..." runs that one command whatever the client asks for, ideal for backup or deploy keys. The command the client requested is available to it in $SSH_ORIGINAL_COMMAND.
  • restrict turns off port, agent and X11 forwarding and terminal allocation in one word. Re-enable single features after it, for example restrict,pty.
  • no-port-forwarding, no-agent-forwarding, no-X11-forwarding and no-pty turn off one feature each, if you prefer to list them.
  • permitopen="localhost:5432" allows port forwarding to that destination only.
  • expiry-time="20271231" makes the key stop working after that date.

To revoke a key, delete its line. Lines starting with # are comments. List fingerprints with ssh-keygen -lf ~/.ssh/authorized_keys.

The location is set by AuthorizedKeysFile in sshd_config. The default is .ssh/authorized_keys .ssh/authorized_keys2, relative to the home directory. Some hardened setups move it to a root-owned path such as /etc/ssh/authorized_keys/%u, in which case editing the file in your home changes nothing.

Permissions sshd requires

With the default StrictModes yes, sshd ignores authorized_keys if it, the .ssh folder, or the home directory can be written by anyone other than the owner. The fix:

chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Ownership matters too: a file root creates in someone else's home belongs to root, and sshd will not trust it. Fix it with chown -R alice: /home/alice/.ssh. On Fedora, RHEL, Rocky and Alma, also restore the SELinux labels with restorecon -R -v /home/alice/.ssh.

Disable password login safely

Key-only login stops password guessing completely. Do it in this order so you cannot lock yourself out:

  1. Confirm that key login works for every account that needs access, including the one you use for sudo.
  2. Keep one SSH session open while you change the configuration. Existing sessions survive a reload.
  3. Put the settings in a drop-in file. Debian, Ubuntu and Fedora include /etc/ssh/sshd_config.d/*.conf at the top of sshd_config, and for each option the first value sshd reads wins. Cloud images often ship 50-cloud-init.conf with PasswordAuthentication yes, which silently overrides a line further down in the main file. A file whose name sorts earlier wins:
sudo tee /etc/ssh/sshd_config.d/10-key-only.conf > /dev/null <<'EOF'
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
EOF
sudo sshd -t && sudo systemctl reload ssh

Use sshd instead of ssh as the service name on Fedora, RHEL and Arch. Then confirm the effective values and test from a new terminal:

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

$ ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password user@host
user@host: Permission denied (publickey).

The refusal is what you want: the server no longer offers passwords at all. Only then close the safety session.

Cloud servers and panels

AWS, Google Cloud, Azure, Hetzner, DigitalOcean and others take a public key when you create a server and install it on first boot. The default user varies: ubuntu on Ubuntu images, ec2-user on Amazon Linux, admin on Debian images on AWS, root on many VPS providers.

  • Adding a key later through the panel usually only affects new servers. For a running one, append it to authorized_keys as above.
  • Google Cloud keeps keys in project or instance metadata in the form username:ssh-ed25519 AAAA... comment and its guest agent writes them into that user's authorized_keys. With OS Login enabled, metadata keys are ignored.
  • cloud-init user data can create users with keys on any provider that supports it:
#cloud-config
users:
  - default
  - name: deploy
    shell: /bin/bash
    sudo: ALL=(ALL) NOPASSWD:ALL
    ssh_authorized_keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... you@laptop

Remove or rotate a key

When a laptop is lost or a colleague leaves, delete their line from authorized_keys on every server and account. Match lines by fingerprint rather than by comment, since comments are free text:

ssh-keygen -lf ~/.ssh/authorized_keys

To rotate your own key, install the new public key first, log in with it, then remove the old line. On many servers this is a job for configuration management or SSH certificates, which the ssh-keygen reference introduces.

Troubleshooting "Permission denied (publickey)"

Work through these in order.

  1. See what the client offers. Run ssh -v user@host. Lines like Offering public key: /home/you/.ssh/id_ed25519 show which keys were tried, and Server accepts key shows success. If your key is never offered, add -i or an IdentityFile line.
  2. Check the account. The key must be in the home of the user you log in as. ssh host uses your local user name, which is often not the remote one.
  3. Check permissions and ownership on the server, as above.
  4. Read the server log. sshd says exactly why it refused a key. Run sudo journalctl -u ssh -n 50 (Debian, Ubuntu) or sudo journalctl -u sshd -n 50, or look in /var/log/auth.log or /var/log/secure. "Authentication refused: bad ownership or modes" points at permissions.
  5. Check for a broken line. A key pasted with line breaks, or a private key pasted by mistake, will not match. Each key must be a single line.
  6. SELinux. On Fedora, RHEL, Rocky and Alma, wrong file labels block sshd even with correct modes. Run restorecon -Rv ~/.ssh.
  7. Old servers and RSA. OpenSSH 8.8 and later clients no longer sign with SHA-1. A server older than OpenSSH 7.2 only understands that, so RSA keys fail against it. Upgrade the server, use an Ed25519 key if it supports one, or, as a last resort, add PubkeyAcceptedAlgorithms +ssh-rsa for that host in your client config.
  8. sshd configuration. Confirm PubkeyAuthentication is not no and that AuthorizedKeysFile points where you think, with sudo sshd -T | grep -i authorizedkeys.

The list in the error tells you what the server still allows: (publickey) means keys only; (publickey,password) means the key failed and you mistyped the password or cancelled the prompt.

Other common errors

"Received disconnect ... Too many authentication failures"

Your agent holds many keys and ssh offered them all before the right one; the server stops after six attempts by default (MaxAuthTries). Name the key for that host and add IdentitiesOnly yes, or run ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519 user@host.

"WARNING: UNPROTECTED PRIVATE KEY FILE!"

This one is on your side: the private key is readable by others and ssh ignores it. Run chmod 600 ~/.ssh/id_ed25519.

"Host key verification failed"

The server presented a host key that differs from the one in your known_hosts, typically after a reinstall. Check the new fingerprint on the server's console, then remove the old entry with ssh-keygen -R host.

"no matching host key type found. Their offer: ssh-rsa"

The server's OpenSSH is too old to sign its host key with SHA-2. Upgrade it; meanwhile HostKeyAlgorithms +ssh-rsa in a Host block for that server lets you connect.

"Connection refused" or "Connection timed out"

The problem is not the key: nothing listens on that port, or a firewall drops the connection. Check the port, that sshd runs (systemctl status ssh), and the provider's firewall or security group.

Windows servers

The OpenSSH Server on Windows reads C:\Users\name\.ssh\authorized_keys for standard users. For members of the Administrators group it reads C:\ProgramData\ssh\administrators_authorized_keys instead, and that file must be writable only by Administrators and SYSTEM. The exact icacls command is in the Windows guide.

FAQ

Where does the public key go on the server?

In ~/.ssh/authorized_keys in the home directory of the account you log in as, for example /home/alice/.ssh/authorized_keys, one key per line. The private key never goes on the server.

Can I add several keys to one account?

Yes. Put each public key on its own line. Any one of them is enough to log in, which is how a team shares a deploy account or how you use one key per device.

Do I need to restart sshd after editing authorized_keys?

No. sshd reads authorized_keys on every login. You only reload the service after changing /etc/ssh/sshd_config or a file in sshd_config.d.

Is ssh-copy-id safe to run twice?

Yes. It first tries to log in with the key and skips keys that already work, so it does not create duplicate lines.

What if password login is already disabled and I have no key on the server?

You need another way in: the cloud provider's console or key field, a rescue system, or someone who already has access appending your public key for you. Send them the .pub file only.

Should root log in with a key?

It is better to log in as a normal user and use sudo. If root must log in directly, PermitRootLogin prohibit-password, the OpenSSH default, allows keys and refuses passwords.

How do I add a key to a server I reach through a jump host?

ssh-copy-id passes options to ssh, so ssh-copy-id -i ~/.ssh/id_ed25519.pub -o ProxyJump=bastion user@internal-host works, as does a ProxyJump line for that host in ~/.ssh/config.