SSH Key Authentication Setup - Passwordless and Secure Login
What you'll be able to do
- Generate an ED25519 key pair, deploy the public key, and log in without a password
- Set the right permissions under `~/.ssh` and diagnose `Permission denied (publickey)` yourself
- Manage multiple servers and passphrases with `~/.ssh/config` and `ssh-agent`
Prerequisites (read these first)
What This Article Covers
- How to generate an SSH key pair and connect to a server
- How to deploy your public key correctly with
ssh-copy-id - How to manage multiple servers with
~/.ssh/config
Terms used in this article (defined up front)
- Key pair: two matching files. The public key (the one ending in
.pub) and the private key (the one without.pub) form one set - Public key: the key you place on the server. It is safe for others to see
- Private key: the key that stays on your own machine. It must never reach anyone else. Also called an "identity file"
- Passphrase: the secret that encrypts the private key file itself. It is not the server login password — these two are easy to confuse, so keep them apart
authorized_keys: the file on the server listing every public key allowed to log in as that user. It lives at~/.ssh/authorized_keysssh-agent: a background program that holds your decrypted private key in memory so you do not retype the passphrase- ED25519 / RSA: names of key algorithms. ED25519 is the current recommendation
Quick Summary (3 Steps)
- Run
ssh-keygen -t ed25519to generate a key pair - Run
ssh-copy-id user@serverto deploy the public key - Run
ssh user@server— no password needed
Prerequisites
- Ubuntu / Debian / RHEL-based Linux on both client and server
- OpenSSH installed on both machines
- Initial access to the server via password authentication
Why Use Key Authentication?
Key authentication is more secure than passwords and removes manual input from automation. There are three main reasons to switch.
- Security: Your private key never leaves your local machine. No password is sent over the network.
- Brute-force resistance: With a long enough key, trying every combination is infeasible.
- Automation: rsync, Ansible, and CI/CD pipelines can connect where nobody is there to type a password.
1. Generate a Key Pair
Which algorithm should you use?
ED25519 is the current recommendation. It gives you strength on par with (or above) RSA-4096 from a far shorter key and faster operations. Fall back to -t rsa -b 4096 only for older devices that cannot handle ED25519.
ssh-keygen -t ed25519 -C "your_email@example.com"
Generating public/private ed25519 key pair. Enter file in which to save the key (/home/user/.ssh/id_ed25519): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/user/.ssh/id_ed25519 Your public key has been saved in /home/user/.ssh/id_ed25519.pub
The -C flag adds a comment (useful for identification). It is optional.
Why set a passphrase?
A passphrase encrypts the private key file itself. Even if your machine is stolen, the key cannot be used as is. Use ssh-agent to avoid retyping the passphrase on every connection (see below). You can press Enter to leave it empty, but then anyone who obtains the private key file can log in straight away.
Verify the generated files:
ls -la ~/.ssh/
total 16 drwx------ 2 user user 4096 May 31 10:00 . drwxr-xr-x 8 user user 4096 May 31 10:00 .. -rw------- 1 user user 419 May 31 10:00 id_ed25519 -rw-r--r-- 1 user user 107 May 31 10:00 id_ed25519.pub
id_ed25519(private key): permissions600. This file stays on your own machine.id_ed25519.pub(public key): the one you append to~/.ssh/authorized_keyson the server.
What you must never do with a private key
If a private key reaches someone else, every server that key opens is compromised. The blast radius is wider than a reused password.
- Never paste it into email, chat, or a ticket: it stays in those logs and histories forever. The thing you share is always the public key (
.pub) - Never place it on a shared server or shared storage: copying a private key to a server with
scpis a classic accident. Only the public key belongs on the server - Never commit it to a repository: once pushed, treat it as leaked even after you rewrite history
- Use a separate key per machine: one key reused everywhere means one lost laptop compromises everything
If you think a key leaked
- Remove that public key's line from
~/.ssh/authorized_keyson every server - Generate a fresh pair with
ssh-keygen -t ed25519and deploy it again - Delete the old key from any service where you registered it (GitHub and similar)
2. Deploy the Public Key to the Server
Use ssh-copy-id (recommended)
Run this once while password authentication is still active on the server. The password it asks for is the server login password, not the key passphrase.
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_ed25519.pub" /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s) /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed user@server's password: Number of key(s) added: 1
ssh-copy-id appends the public key to ~/.ssh/authorized_keys on the server. Existing keys are appended to, never overwritten. If it has to create ~/.ssh, it creates it with the right permissions — but it does not fix permissions on a directory or file that already exists. Always run the permission check in the next section.
Manual deployment when ssh-copy-id is unavailable
cat ~/.ssh/id_ed25519.pub | ssh user@server \ "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Do not turn >> (append) into > (overwrite)
cat >> ~/.ssh/authorized_keys above appends. With a single > it overwrites, wiping every other public key already registered on that server — including the ones your teammates and CI use. Recovering from that needs console access.
Check that the file you send ends in .pub. Drop the .pub and you have just copied your private key to the server.
3. Set Correct Permissions
SSH rejects key authentication when directory or file permissions are too permissive. The reasoning: a key stored where others can read or write it cannot be trusted.
| Path | Required Permission | Reason |
|---|---|---|
~/.ssh/ |
700 |
Owner read/write/execute only |
~/.ssh/authorized_keys |
600 |
Owner read/write only |
~/.ssh/id_ed25519 |
600 |
Private key must be strictly protected |
~/.ssh/id_ed25519.pub |
644 |
Public key is safe for others to read |
Fix commands:
chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chmod 600 ~/.ssh/id_ed25519
Permissions that are too loose break key authentication — but which side rejects you depends on where they are loose.
- Server side: if your home directory,
~/.ssh, orauthorized_keysis writable by group or others (775,777, and so on), sshd ignores that key and returnsPermission denied (publickey).755and644are not rejected - Client side: if the private key carries any group or other permission at all (
644,755),sshprintsUNPROTECTED PRIVATE KEY FILEand refuses to use the key. That refusal comes from your localssh, not from the server
When a connection fails, start by checking the values in the table above.
Do not "fix" this with chmod 777 ~/.ssh. Loosening permissions makes it worse: every user on the host could then append their own public key to your authorized_keys and log in as you. The correct fix is to match the values in the table above (700 / 600 / 644).
4. Test the Connection
ssh -v user@server
The -v flag (verbose) prints the authentication process in detail. It shows which key was offered and whether the server accepted it.
... debug1: Offering public key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxxxx debug1: Server accepts key: /home/user/.ssh/id_ed25519 ED25519 SHA256:xxxxx Authenticated to server ([x.x.x.x]:22) using "publickey".
Authenticated to server ... using "publickey" confirms key authentication is working.
Disable password authentication only after key login is proven
Do not set PasswordAuthentication no in /etc/ssh/sshd_config until key authentication reliably works. Disable it while the key is misplaced and you end up with a server nobody can log into.
The safe sequence
- Keep your current SSH session open. Do not close it
- From a second terminal, open a new session and confirm the key login works
- Only then change the setting and run
sudo systemctl reload ssh(sshdon RHEL-family systems) - Confirm login works from yet another new session before closing the first one
Troubleshooting
Symptom: Permission denied (publickey)
Check 1: is a key being offered at all?
ssh -v user@server 2>&1 | grep -i "offering\|authentications that can continue"
If none is offered, name the key explicitly with ssh -i ~/.ssh/id_ed25519 user@server, or check IdentityFile in ~/.ssh/config.
Check 2: permissions on the private key, client side
ls -l ~/.ssh/id_ed25519
Anything other than -rw------- (600) needs chmod 600 ~/.ssh/id_ed25519.
Check 3: permissions and contents on the server
ssh user@server 'ls -ld ~ ~/.ssh; ls -l ~/.ssh/authorized_keys; wc -l ~/.ssh/authorized_keys'
If the home directory, ~/.ssh, or authorized_keys is writable by group or others, sshd ignores the key.
Fix
- Bring the permissions back to
700/600 - Run
ssh-copy-idagain - If it still fails, read the reason on the server with
sudo journalctl -u ssh -n 30(-u sshdon RHEL-family systems)
5. Manage Connections with ~/.ssh/config
Once you have more than a couple of hosts, use ~/.ssh/config. It lets you save a hostname, username, and key file under one short alias.
vim ~/.ssh/config
Example configuration:
Host myserver
HostName 192.168.1.100
User ubuntu
IdentityFile ~/.ssh/id_ed25519
Port 22
Host staging
HostName staging.example.com
User deploy
IdentityFile ~/.ssh/id_ed25519After saving, connect with just ssh myserver.
chmod 600 ~/.ssh/config
If ~/.ssh/config is writable by group or others (664, for instance), ssh prints Bad owner or permissions on /home/user/.ssh/config and aborts the connection — it does not quietly carry on. Keep it at 600.
6. Manage Passphrases with ssh-agent
ssh-agent holds your decrypted key in memory so you type the passphrase only once per session. The key lives in memory only and is gone when you log out.
# Start the agent eval "$(ssh-agent -s)" # Add your key (enter passphrase once) ssh-add ~/.ssh/id_ed25519
Identity added: /home/user/.ssh/id_ed25519 (your_email@example.com)
List registered keys:
ssh-add -l
On macOS, the system Keychain integrates with ssh-agent. Add UseKeychain yes and AddKeysToAgent yes to ~/.ssh/config to persist keys across reboots.
Summary
| Command | Purpose |
|---|---|
ssh-keygen -t ed25519 |
Generate key pair (ED25519) |
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server |
Deploy public key to server |
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys |
Fix directory and file permissions |
ssh -v user@server |
Test connection with verbose output |
eval "$(ssh-agent -s)" && ssh-add |
Cache passphrase for the session |