Setting up SSH key authentication on Linux VPS
What You’ll Need
- Hetzner VPS or Contabo VPS for hosting your Linux server
- DigitalOcean as an alternative hosting provider
- A local machine running Linux, macOS, or Windows with WSL2
- SSH client (built-in on Linux/macOS, included with Windows 10+)
- Basic command-line familiarity
Table of Contents
- What You’ll Need
- Why SSH Keys Beat Passwords
- Generating Your SSH Keypair Locally
- Uploading Your Public Key to the VPS
- Hardening SSH Configuration
- Testing and Troubleshooting
- Getting Started
- Outsource Your Automation
Why SSH Keys Beat Passwords
I’ve managed dozens of VPS instances across hosting providers, and I can tell you with certainty: password-based SSH authentication is the security equivalent of leaving your front door unlocked. SSH keys eliminate brute-force attacks, eliminate password reuse risks, and let you authenticate without ever transmitting a password across the network. They’re cryptographically stronger, faster to use once configured, and they’re the industry standard for good reason.
The basic principle is simple. You generate a keypair: a private key (stays on your local machine, never shared) and a public key (goes on the server). When you connect, the server challenges you with data encrypted by your public key. Only your private key can decrypt it, proving your identity without password transmission. It’s beautiful cryptography in action.
If you’re planning to run production workloads on your VPS, SSH keys are non-negotiable. Whether you’re building web scraping data pipelines on VPS or running background jobs with scheduled tasks, you need secure, passwordless authentication that plays well with automation scripts and CI/CD systems.
Generating Your SSH Keypair Locally
I always start on my local machine. Let me walk you through the complete process, whether you’re on Linux, macOS, or Windows with WSL2.
Open your terminal and run this command:
ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/id_ed25519
Let me break down what’s happening here. The -t ed25519 flag specifies the ED25519 algorithm, which is modern, compact, and more secure than the older RSA algorithm. The -C flag adds a comment (I use my email, but it can be anything) to identify the key. The -f flag specifies exactly where to save the key and what to name it. I’m using ~/.ssh/id_ed25519, which is the standard location.
When prompted for a passphrase, I recommend entering one. This passphrase encrypts your private key on disk. If someone gets your machine, they can’t use your key without the passphrase. Enter your passphrase (it won’t echo to the screen):
Enter passphrase (empty for no passphrase):
Then confirm it:
Enter same passphrase again:
The command generates two files:
~/.ssh/id_ed25519 (private key, keep this secret)
~/.ssh/id_ed25519.pub (public key, this goes on the server)
Let me verify the keys were created correctly:
ls -la ~/.ssh/id_ed25519*
You should see output similar to:
-rw------- 1 user group 399 Dec 15 10:24 /home/user/.ssh/id_ed25519
-rw-r--r-- 1 user group 95 Dec 15 10:24 /home/user/.ssh/id_ed25519.pub
Notice the permissions: the private key has 600 (readable/writable only by you), and the public key has 644 (readable by everyone). This is correct and essential for security.
Now let’s see the actual content of the public key:
cat ~/.ssh/id_ed25519.pub
Output will look something like:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKp7q8X3Zx9mQ2r4L5dY6vN8oP1sT2uV3wX4yZ5aB6cD your-email@example.com
Copy this entire line (the ssh-ed25519 part through your email comment). You’ll paste it on the server.
If you ever need to view your private key’s fingerprint to verify it:
ssh-keygen -l -f ~/.ssh/id_ed25519
This outputs a hash like:
256 SHA256:aBcDeFgHiJkLmNoPqRsTuVwXyZ1a2b3c4d5e6f7g8h your-email@example.com (ED25519)
This fingerprint is useful when you need to verify a key in your SSH config or when troubleshooting connections.
Uploading Your Public Key to the VPS
Now I need to get my public key onto the server. I’m going to assume you have a fresh VPS from Hetzner VPS, Contabo VPS, or DigitalOcean, and you’ve received root credentials via email or the hosting dashboard.
First connection requires a password, which is fine since we’re only doing it once. Connect to your VPS:
ssh root@YOUR_VPS_IP_ADDRESS
Replace YOUR_VPS_IP_ADDRESS with your actual IP. You’ll be prompted for the root password from your hosting provider:
The authenticity of host 'XXX.XXX.XXX.XXX (XXX.XXX.XXX.XXX)' can't be established.
ED25519 key fingerprint is SHA256:xxxxxxxxxxxxx.
This key is added to ECDSA key fingerprint is SHA256:xxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Type yes and hit enter. Then paste your root password.
Now that I’m on the server, I need to set up the .ssh directory and the authorized_keys file if they don’t exist:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Let me explain these permissions. The .ssh directory itself needs 700 (read, write, execute for owner only). The authorized_keys file needs 600 (read and write for owner only). SSH is picky about permissions for security reasons. If these are wrong, SSH authentication won’t work.
Now, append your public key to the authorized_keys file:
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKp7q8X3Zx9mQ2r4L5dY6vN8oP1sT2uV3wX4yZ5aB6cD your-email@example.com" >> ~/.ssh/authorized_keys
Paste your actual public key (the one you copied from your local machine). The >> operator appends to the file without overwriting existing content, which matters if you’re adding multiple keys.
Verify it was added:
cat ~/.ssh/authorized_keys
You should see your public key displayed. If you’re planning to add more keys later (for other team members or machines), you can repeat this echo command. Each key should be on its own line.
💡 Fast-Track Your Project: Don’t want to configure this yourself? I build custom n8n pipelines and bots. Message me with code SYS3-HUGO.
Hardening SSH Configuration
At this point, you have key authentication working. But we’re not done. I need to harden the SSH configuration to disable password authentication entirely and improve security overall.
While still logged in as root, open the SSH config file:
nano /etc/ssh/sshd_config
Or if you prefer vim:
vim /etc/ssh/sshd_config
I’m looking for and modifying these specific lines. First, find the PermitRootLogin line. It probably looks like:
#PermitRootLogin prohibit-password
Change it to:
PermitRootLogin no
This disables root login via SSH entirely. Root should never log in remotely.
Next, find the PubkeyAuthentication line:
#PubkeyAuthentication yes
Uncomment it (remove the #) and ensure it says yes:
PubkeyAuthentication yes
Now find the PasswordAuthentication line:
#PasswordAuthentication yes
Uncomment it and change it to:
PasswordAuthentication no
This disables password-based SSH authentication. Only key authentication works now.
Find the AuthorizedKeysFile line. It probably looks like:
AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
This is fine as-is. It tells SSH where to look for your public keys.
I also recommend these additional security settings. Find or add these lines:
PermitEmptyPasswords no
MaxAuthTries 3
MaxSessions 5
ClientAliveInterval 300
ClientAliveCountMax 2
Let me explain what these do:
PermitEmptyPasswords no: Prevents anyone from logging in with a blank password (good practice even with password auth disabled)MaxAuthTries 3: Limits authentication attempts to 3 before disconnecting (stops brute-force)MaxSessions 5: Limits the number of concurrent sessions per connectionClientAliveInterval 300: Sends a keep-alive message every 300 secondsClientAliveCountMax 2: Closes the connection after 2 missed keep-alives
Save and exit. In nano, press Ctrl+X, then Y, then Enter. In vim, press Escape, type :wq, and hit Enter.
Now restart the SSH daemon to apply changes:
systemctl restart ssh
Or on some systems:
systemctl restart sshd
Check that SSH restarted successfully:
systemctl status ssh
You should see “active (running)” in the output. If it failed, there might be a syntax error in your config file. You can check with:
sshd -t
This performs a configuration test without restarting. If there’s an error, it will tell you.
Before you log out, open a new terminal window on your local machine and test the connection with your SSH key:
ssh -i ~/.ssh/id_ed25519 root@YOUR_VPS_IP_ADDRESS
Wait, I just disabled root login. Let me create a regular user first. Back on the VPS (still logged in as root), create a new user:
adduser deploy
You’ll be prompted for a password and personal information. The password is just for the local sudo command, not for remote login (since we disabled password authentication). Set a strong one anyway:
Adding user `deploy' ...
Adding new group `deploy' (1001) ...
Adding new home directory `/home/deploy' ...
Copying files from `/etc/skel' ...
New password:
Retype new password:
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [y/N]
Press Enter through all the prompts except the password fields, then press y at the end.
Now add this user to the sudo group so they can run administrative commands:
usermod -aG sudo deploy
Now switch to the deploy user:
su - deploy
And set up SSH keys for this user too:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
Paste your public key into the authorized_keys file:
echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKp7q8X3Zx9mQ2r4L5dY6vN8oP1sT2uV3wX4yZ5aB6cD your-email@example.com" > ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Note: I used > (overwrite) instead of >> (append) because this is the first key for this user.
Now log out:
exit
Then exit from root:
exit
Testing and Troubleshooting
Back on your local machine, test connecting as the deploy user:
ssh -i ~/.ssh/id_ed25519 deploy@YOUR_VPS_IP_ADDRESS
You should connect without a password prompt. If your SSH key has a passphrase (which I recommended), you’ll be prompted for that passphrase, but not for a server password.
If you get Permission denied (publickey), there are several common issues:
First, verify the server logs:
ssh -i ~/.ssh/id_ed25519 -v deploy@YOUR_VPS_IP_ADDRESS
The -v flag enables verbose output. Look for lines like Offering public key and Authentication succeeded or error messages.
Make sure file permissions are exactly right:
stat -c '%a %n' ~/.ssh
stat -c '%a %n' ~/.ssh/authorized_keys
On your local machine, verify your private key isn’t world-readable:
stat -c '%a %n' ~/.ssh/id_ed25519
Should show 600.
Make sure your public key is actually in the authorized_keys file:
cat ~/.ssh/authorized_keys
Copy-paste errors happen. Verify the key starts with ssh-ed25519 and ends with your comment.
If you’re still having trouble, check the SSH daemon is actually listening:
sudo netstat -tlnp | grep sshd
Or with newer systems:
sudo ss -tlnp | grep sshd
You should see it listening on port 22.
For convenience, create an SSH config file on your local machine so you don’t need to remember the IP address or username:
nano ~/.ssh/config
Add this:
Host myvps
HostName YOUR_VPS_IP_ADDRESS
User deploy
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
Now you can simply connect with:
ssh myvps
SSH will use all the settings automatically.
If you need to manage multiple servers or add multiple keys for team members, this config file becomes invaluable. Each Host block can have its own settings.
Getting Started
Ready to set up SSH key authentication? Start with your VPS provider of choice:
- Hetzner VPS offers reliable, affordable infrastructure
- Contabo VPS provides competitive pricing with good performance
- DigitalOcean has excellent documentation and support
Once your VPS is running Linux (Ubuntu 22.04 LTS is my standard choice), follow the steps above. The entire process takes about 15 minutes the first time.
When you’re ready to automate workflows on your VPS, SSH keys integrate seamlessly with deployment tools, CI/CD systems, and automation frameworks. If you’re scheduling Python jobs with APScheduler and Redis or building structured log pipelines with Fluent Bit, passwordless SSH authentication is what makes those systems secure and scalable.
Outsource Your Automation
Don’t have time? I build production n8n workflows, WhatsApp bots, and fully automated YouTube Shorts pipelines. Hire me on Fiverr, mention SYS3-HUGO for priority. Or DM at chasebot.online.
Want to automate this yourself?
Start with n8n Cloud (free tier available) or self-host on a Hetzner VPS for full control.
Want this engine running on your own VPS?
This blog publishes itself — daily, unattended, on free API tiers. The full engine, Hugo theme, and setup guide are available as System 3.
Get System 3