SSH key-based authentication without password: complete Ed25519 guide
Set up passwordless SSH authentication using Ed25519 or RSA keys, disable password login, and protect Linux servers against brute force attacks.
Password-based SSH is the largest brute force attack surface any Linux server exposed to the internet faces. Logs from any VPS with port 22 open to the internet show thousands of attempts per day coming from botnets scanning entire IP ranges looking for common combinations. The definitive defense is not a stronger password — it’s eliminating password authentication from the server and accepting only cryptographic keys.
This tutorial is for sysadmins and developers who manage Linux VPS or dedicated servers and want to migrate to public-key authentication. You’ll generate an Ed25519 key pair on your local machine, copy the public key to the server, validate key-based login in a parallel session, and only then safely disable PasswordAuthentication — the procedure prevents you from being locked out if something goes wrong in the configuration.
Estimated time: 15-20 minutes from scratch, assuming you already have active SSH access via password on the target server.
Prerequisites
Active SSH access to the server via password (root or sudo user), a local Linux/macOS/WSL machine with OpenSSH installed (ssh -V should return 8.0 or higher), and two terminals available on your local machine — we’ll keep the original session open as a “safety net” while we validate the new configuration in a second session.
Ed25519 256 bits 6.5+ (2014) ~/.ssh/authorized_keys 700 600 Generating the Ed25519 key pair
Key generation happens on your local machine (client), not on the server. The private key must never leave the machine where it was generated — only the public key circulates. Ed25519 is the modern standard: 32-byte keys, faster signing than RSA-4096, and cryptographic strength equivalent to RSA-3072.
Open the terminal on your local machine and run:
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519_hostiniThe -t ed25519 parameter sets the algorithm, -C adds an identifier comment (usually your email — it gets embedded in the public key and helps identify the origin in key lists), and -f sets the file name. Use a specific name instead of the default id_ed25519 if you manage multiple servers and want to separate keys.
Set a strong passphrase when prompted:
Enter passphrase (empty for no passphrase): [type strong passphrase]
Enter same passphrase again: [confirm]The passphrase encrypts the private key on disk — if someone copies the ~/.ssh/id_ed25519_hostini file, they still need the passphrase to use it. Don’t skip this step. Use 4-6 random words or a long phrase — ssh-agent (next section) makes you type this once per session, not on every login.
Confirm that the two files were created:
ls -la ~/.ssh/id_ed25519_hostini*You should see id_ed25519_hostini (private, permission 600) and id_ed25519_hostini.pub (public, permission 644). The private key NEVER leaves this machine — don’t send it by email, don’t push it to Git, don’t copy it to a USB stick. Backups only in an encrypted password manager or offline vault.
Copying the public key to the server
The canonical method is ssh-copy-id, which adds the public key to the remote user’s ~/.ssh/authorized_keys, creating the file with correct permissions if it doesn’t exist. You’ll need to type the current SSH password one last time.
Run on the local machine (replace user and IP):
ssh-copy-id -i ~/.ssh/id_ed25519_hostini.pub [email protected]If you use a non-standard SSH port: ssh-copy-id -i ~/.ssh/id_ed25519_hostini.pub -p 2222 [email protected]. The command SSHes into the server (authenticating by password), appends the public key to ~/.ssh/authorized_keys, sets ~/.ssh permissions to 700 and the file to 600 — any more permissive permission causes sshd to reject the key silently.
If ssh-copy-id is not available (rare on older macOS), use the manual method:
cat ~/.ssh/id_ed25519_hostini.pub | ssh [email protected] \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"This pipe sends the public key content via stdin, creates the .ssh folder with correct permissions if it doesn’t exist, appends the key to the authorized_keys file (without overwriting previous keys), and sets the final permission.
Validating key-based login before changing the server
This step is critical. You need to confirm key-based login works BEFORE disabling the password — otherwise, any permission error or typo on the server locks you out.
Open a SECOND terminal (keep the first one logged in as a safety net) and try connecting using the key:
ssh -i ~/.ssh/id_ed25519_hostini [email protected]You will be prompted to enter the key’s passphrase (not the server password). If you log in successfully and land in the server shell, key-based authentication is working. If the server asks for the user’s password instead of the passphrase, something is wrong — do not proceed.
Check on the server: ls -la ~/.ssh/ should show the folder with permission 700 and authorized_keys with 600. File ownership must be the user itself (not root). Logs in /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (RHEL/Rocky) show the exact reason for rejection — usually “bad ownership or modes”.
Configuring ssh-agent to avoid typing the passphrase all the time
Without ssh-agent, you type the passphrase on every SSH connection — which is tedious and leads to short, weak passphrases. The agent keeps the key decrypted in memory during the session.
Add the key to the agent on the local machine:
ssh-add ~/.ssh/id_ed25519_hostiniOn macOS, use ssh-add --apple-use-keychain ~/.ssh/id_ed25519_hostini to integrate with Keychain — the passphrase is stored permanently and the agent opens without prompts on reboots. On Linux, add eval "$(ssh-agent -s)" to ~/.bashrc if the agent doesn’t start automatically.
Create a ~/.ssh/config file to simplify connections:
Host hostini-prod
HostName 45.xxx.xxx.xxx
User user
Port 22
IdentityFile ~/.ssh/id_ed25519_hostini
IdentitiesOnly yesNow you connect with ssh hostini-prod instead of the full command. IdentitiesOnly yes forces the use of only the specified key — without it, the agent offers ALL loaded keys to the server, which can cause “Too many authentication failures” on servers with low MaxAuthTries.
Disabling password authentication on the server
With key-based login validated, it’s now safe to disable password authentication. Edit the /etc/ssh/sshd_config file on the server.
Connect to the server through the validated session (key) and edit the configuration:
sudo nano /etc/ssh/sshd_configFind and adjust the following directives (uncomment by removing the # if necessary):
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
ChallengeResponseAuthentication no
UsePAM yesPermitRootLogin prohibit-password allows root via key but blocks root via password — useful for automation that needs root. If you don’t need root via SSH (recommended), use PermitRootLogin no and run sudo from a regular user.
Validate the syntax before restarting the service:
sudo sshd -tEmpty output = valid configuration. If an error appears, DO NOT restart sshd — fix it first. Restarting sshd with an invalid config can leave the server without active SSH until reboot.
Restart the SSH service:
sudo systemctl restart sshdOn recent Ubuntu/Debian, the service may be called ssh instead of sshd — sudo systemctl restart ssh. The current session is NOT disconnected by the restart — sshd only applies the new rules to new connections.
Keep the current session open. Open a THIRD terminal and try connecting from scratch with your key. If it works, the configuration is complete. If it fails, use the original session (still open) to revert PasswordAuthentication yes and restart sshd. Only close all sessions after validating that new connections work.
Final verification
Confirm the server now rejects passwords:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no [email protected]
Expected output: Permission denied (publickey). This command forces the client to attempt only password, ignoring keys — if the server responds by refusing, it’s correctly configured to accept only public keys. Also try the normal connection with a key (ssh hostini-prod) — it should log in without a server password prompt (only passphrase if the agent doesn’t have the key).
Troubleshooting
”Permission denied” even with the correct key
Check permissions on the server (most common cause):
stat -c "%a %n" ~/.ssh ~/.ssh/authorized_keys
# Expected: 700 /home/user/.ssh
# 600 /home/user/.ssh/authorized_keys
If different, fix with chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys. SELinux on RHEL/Rocky servers can block reading even with correct permissions — restorecon -R ~/.ssh applies the correct context.
”Too many authentication failures”
Happens when ssh-agent tries several keys before reaching the right one. Force only the correct key:
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_hostini user@server
Or add IdentitiesOnly yes to the local machine’s ~/.ssh/config as shown in step 8.
Locked out after disabling password
Use the VPS panel’s web console to access the server directly (bypass SSH), edit /etc/ssh/sshd_config reverting PasswordAuthentication yes, restart the service, and investigate the problem calmly. On providers that offer serial console, this is the official recovery method.
Next steps
- Configure fail2ban to block suspicious attempts on other surfaces (HTTP, SMTP) — SSH no longer needs it with password disabled
- Change the default SSH port (22 → 2222 or similar) — reduces log noise by ~95%, although it doesn’t increase real security
- Configure 2FA via TOTP on SSH with
pam_google_authenticatorfor an additional layer on critical servers - Document the authorized keys on each server — abandoned keys from ex-employees are a real incident vector
- Consider SSH certificates with your own CA if you manage a fleet of 20+ servers — scales better than distributing keys manually
If you’re putting this into production on a Hostini VPS, all VPS plans come with an integrated emergency web console in the panel — a safe fallback in case an SSH configuration locks you out.
Frequently asked questions
Ed25519 or RSA: which one to choose in 2026?
Ed25519 is the recommended standard for any new server — 32-byte keys, instant generation, faster signing than RSA-4096, and equivalent cryptographic strength. Use RSA-4096 only if you need to interoperate with legacy systems (OpenSSH < 6.5, some older network appliances) that still don't support Ed25519. RSA-2048 is no longer acceptable for production.
Can I use the same SSH key across multiple servers?
Technically yes, and it's common to use one key per client machine (laptop, desktop) replicated across N servers. The risk: if the client is compromised, the attacker gets access to everything. Mitigation: protect the private key with a strong passphrase and use ssh-agent. More sensitive environments (critical production) can isolate with one key per server group.
What happens if I lose my private key?
Without the private key and without another access method, you're locked out — there's no cryptographic recovery. That's why the tutorial insists on keeping PasswordAuthentication active until you validate key-based login in a parallel session. On VPS, you always have the panel's web console as an emergency fallback to revert the configuration.
Do I need to disable password login even with fail2ban installed?
Yes. Fail2ban reduces the rate of attempts but doesn't eliminate the vector — an attacker with a distributed botnet of thousands of IPs bypasses the attempt threshold. Disabling PasswordAuthentication eliminates the entire category of brute force attacks against passwords. Fail2ban remains useful for other surfaces (HTTP, SMTP).
How does ssh-agent work and why is it worth using?
ssh-agent is a daemon that keeps your keys decrypted in memory during the session, so you type the passphrase once and use the key N times without retyping. On macOS the agent integrates with Keychain; on Linux, it starts with the graphical session. On intermediate servers, ssh-agent with ForwardAgent lets you jump to internal hosts without copying the private key — but use it sparingly for security.
Can I restrict an SSH key to specific commands?
Yes. The ~/.ssh/authorized_keys file accepts options before the key: command="rsync ..." forces execution of a single command, from="1.2.3.4" restricts source IPs, no-port-forwarding and no-X11-forwarding disable features. Useful for automation keys (backup, deploy) that should not provide an interactive shell if the key leaks.