How to Harden SSH on an Ubuntu VPS (2026 Guide)

The moment an Ubuntu VPS gets a public IP, automated bots start knocking on port 22. Within hours you will see thousands of failed login attempts in your auth log — scripts cycling through root, admin, ubuntu, and a dictionary of weak passwords. SSH is the front door to your server, and the default configuration leaves it wider open than it needs to be. This guide walks through hardening sshd on Ubuntu 22.04 so that the door only opens for you.

Quick Verdict
The three changes that eliminate the overwhelming majority of SSH attacks are: switch to key-only authentication, disable direct root login, and restrict logins to named users. Changing the port is cosmetic but cuts log noise. Make every change in a second SSH session you keep open, so a typo in sshd_config never locks you out of your own server.

Set Up Key-Based Authentication First

Before disabling passwords, you need a working key pair, or you will lock yourself out. On your local machine — not the server — generate an ed25519 key, which is faster and more secure than older RSA keys:

1ssh-keygen -t ed25519 -C "you@example.com"

Accept the default path (~/.ssh/id_ed25519) and set a passphrase. The passphrase encrypts the private key on disk, so a stolen laptop does not hand over server access.

Copy the public half to the VPS:

1ssh-copy-id -i ~/.ssh/id_ed25519.pub youruser@your-server-ip

If ssh-copy-id is unavailable, append the contents of id_ed25519.pub to ~/.ssh/authorized_keys on the server manually, then fix permissions:

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

Now confirm you can log in with the key — ssh youruser@your-server-ip should connect after asking only for the key passphrase, never the account password. Do not proceed until this works.


Edit sshd_config Safely

All server-side SSH behavior lives in /etc/ssh/sshd_config. Open it with root privileges:

1sudo nano /etc/ssh/sshd_config

Find and set each of the following directives. If a line is commented out with a #, remove the # and change the value:

1PermitRootLogin no
2PasswordAuthentication no
3PubkeyAuthentication yes
4KbdInteractiveAuthentication no
5UsePAM yes

PermitRootLogin no stops anyone from logging in directly as root — attackers have to first compromise a known username and its key, then escalate, which is a far harder target. PasswordAuthentication no is the change that actually stops the brute-force flood: with passwords disabled, a bot guessing credentials simply cannot get in, because there is no password to guess.

On Ubuntu 22.04, watch for a gotcha: a file at /etc/ssh/sshd_config.d/50-cloud-init.conf (present on many cloud images) may re-enable PasswordAuthentication yes and override your main config. Check it:

1sudo grep -r PasswordAuthentication /etc/ssh/sshd_config.d/

If it sets the value, edit that file too, or your change in the main config will be silently ignored.


Restrict Who Can Log In

By default any account with a shell can attempt SSH. Limit it to the specific users who actually need remote access with an AllowUsers directive at the bottom of sshd_config:

1AllowUsers deploy admin

Only deploy and admin can now open an SSH session; every other account is refused before authentication even starts. If you manage groups, AllowGroups sshusers achieves the same with a group instead of a list.


Change the Default Port (Optional)

Moving SSH off port 22 does not make you meaningfully harder to breach — a port scan finds the new port in seconds — but it dramatically reduces log noise from untargeted bots, which only hammer 22. Pick a high port and set it:

1Port 2222

If you run a firewall (you should — see our UFW guide), open the new port before restarting SSH, or you will be locked out:

1sudo ufw allow 2222/tcp

Tighten the Authentication Limits

A few more sshd_config directives reduce the window an attacker has on each connection. Add them alongside your earlier changes:

1MaxAuthTries 3
2LoginGraceTime 20
3MaxSessions 4

MaxAuthTries 3 drops the connection after three failed attempts instead of the default six, halving how many guesses a bot gets per connection. LoginGraceTime 20 disconnects a client that has not authenticated within twenty seconds, so half-open connections from slow scanners do not linger and consume resources. MaxSessions 4 caps how many session channels a single connection can open — ample for normal use, a brake on certain abuse patterns.

These do not replace key-only auth; they trim the edges around it. With passwords already disabled, their main effect is cutting resource use from the constant background scanning.


Apply and Verify Without Locking Yourself Out

This is where people break their own access. Keep your current SSH session open. First, validate the config syntax — this catches typos before they take effect:

1sudo sshd -t

If it prints nothing, the syntax is valid. Now restart the service:

1sudo systemctl restart ssh

In a new terminal, open a second connection to confirm everything works:

1ssh -p 2222 deploy@your-server-ip

If that succeeds, you are hardened. If it fails, your original still-open session is your safety net — fix the config and restart again. Only close the original session once the new one connects cleanly.


Verify the Attack Surface Shrank

Check that root login and password auth are genuinely refused. From a machine without your key, an attempt should be rejected immediately:

1ssh root@your-server-ip
2# Permission denied (publickey).

That publickey rejection — with no password prompt — confirms the server is no longer accepting passwords at all. Watch the auth log to see brute-force attempts now bounce off harmlessly:

1sudo journalctl -u ssh --since "1 hour ago"

What This Buys You

With key-only authentication, no root login, and a restricted user list, the automated attacks that fill server logs become irrelevant — they are guessing passwords against a server that has none to accept. The realistic remaining threat is theft of your private key, which is why the passphrase in step one matters: an encrypted key is useless without it.

For layered defense, pair this with a host firewall (UFW) to control which ports are reachable at all, and Fail2ban to ban IPs that probe other services. SSH hardening is the single highest-value security task on a new VPS — it takes ten minutes and closes the door that attackers try first.

Posts in this series