Blog / Tutorials

Hardening SSH on a VPS — What Matters and What's Cargo Cult

By the NoctHost TeamAugust 26, 20267 min read

Spin up a fresh VPS, wait an hour, and check your auth log. You'll find thousands of login attempts from IPs all over the planet, hammering `root` with `123456` and `admin` and every leaked password on earth. This is the background radiation of the internet, and it panics people into a checklist of "hardening" steps, half of which do nothing.

The good news is that the steps that matter are few, boring, and take five minutes. The steps that don't matter are the ones that get written about the most. Let me separate them, because a false sense of security is worse than none.

The only three that carry the weight

Almost all of your real protection comes from three settings. Get these right and the wall of bot traffic in your logs becomes completely harmless noise.

First, use keys instead of passwords, and then forbid passwords entirely. A password can be guessed. A properly generated SSH key cannot be, not in the lifetime of the universe. Once you've confirmed your key works, this line is what closes the door:

PasswordAuthentication no

With that set, every one of those thousands of bots is trying a door that has no keyhole. They can guess forever and never get in. This single line does more than everything else on this page combined.

Second, stop root from logging in directly:

PermitRootLogin no

You log in as a normal user and escalate with sudo. Why it helps: root is the one username every attacker already knows exists on every Linux box. Forcing them to guess both a valid username and a key they can't produce removes the free half of the problem. It also means every privileged action goes through sudo, which logs who did what.

Third — and this isn't an sshd setting — keep the machine patched. The scary SSH vulnerabilities of the last few years were fixed by an apt upgrade, not by any config tweak. Unattended security updates are worth more than any single line below:

apt install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

That's the core. Everything after this is refinement or theater.

Do the key part right

Weak keys undercut everything, so generate a good one on your own machine, not the server:

ssh-keygen -t ed25519 -a 100 -C "you@laptop"

Ed25519 is the modern default: short, fast, and strong. Skip RSA unless something ancient forces it, and if it does, never go below 4096 bits. Copy the public half up:

ssh-copy-id you@your-server-ip

Then log in with the key in a second terminal and confirm it works before you disable passwords. Do not skip that confirmation. Locking out password auth while your key is broken is the single most common way people fatfinger themselves out of their own server. Keep the first session open until the second one succeeds.

The full sshd_config that matters

Put together, the settings worth setting live in /etc/ssh/sshd_config (or a drop-in file under /etc/ssh/sshd_config.d/):

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no

That fourth line closes a side door: some distros ship keyboard-interactive auth enabled, which can be another password-ish path. Turn it off so PasswordAuthentication no isn't quietly bypassed. Apply changes with:

sshd -t && systemctl reload ssh

The sshd -t tests the config first. Run it every time. A typo in this file plus a reload can refuse all future logins, and sshd -t catches the typo before it bites.

Tip — Always keep one SSH session open while you change SSH config. If a change locks you out, that live session is your undo button — reload, test, and only then trust it.

Now the cargo cult

Here's where I'll annoy people. These are the steps that feel like security and mostly aren't.

Moving SSH off port 22. This is the most popular "hardening" tip and it protects you from essentially nothing. An attacker who can't guess your key doesn't care what port it's on, and a real targeted scan finds your SSH on port 2222 in seconds. What changing the port actually does is quiet your logs, because the dumb broad-sweep bots only hit 22. That's a legitimate reason to do it — less noise makes real events easier to spot — but call it what it is: noise reduction, not risk reduction. Don't let it stand in for PasswordAuthentication no.

Port knocking. You configure the firewall to open SSH only after a secret sequence of connection attempts to closed ports. It's clever, it's fun to set up, and it adds fiddly failure modes for a threat model you already closed with key-only auth. If a bot can't authenticate anyway, hiding the port from it buys you very little for real complexity. Skip it unless you enjoy it as a hobby.

Fail2ban as your main defense. Fail2ban watches your logs and bans IPs that fail too many times. It's fine, and it does trim log noise and stop the dumbest floods. But notice: with password auth already disabled, those failing IPs were never going to get in. Fail2ban is banning people from a locked door. It's a reasonable nice-to-have for tidiness and to blunt resource use from aggressive scanners — it is not the thing keeping you safe, and treating it as your primary control means you've mistaken the janitor for the lock.

The pattern across all three: they operate on attackers who already can't succeed. Real security came from removing the thing they were attacking (the password). These add friction, mostly to your logs, sometimes to yourself.

What's actually worth adding on top

If you've done the core and want to go further, these earn their keep:

  • A firewall that default-denies inbound and only opens what you use. ufw default deny incoming, then allow SSH and your actual services. This limits blast radius if some other service is the weak point.
  • Better still, don't expose SSH to the internet at all. Put it behind a WireGuard VPN and bind sshd to the VPN interface, so the public internet can't even reach the SSH port. This is a genuine step up from every myth above, because it removes SSH from the attack surface entirely instead of just filtering who reaches it.
  • AllowUsers you to whitelist exactly which accounts can log in, so a stray service account can never be an SSH entry point.

Notice these change what's reachable and what's exposed. That's the difference between hardening and theater: real hardening shrinks the attack surface, theater decorates it.

Spin one up in about a minute

Email signup, pay with crypto, hourly billing. Trying a box costs cents — destroy it when you are done.

Deploy a server

Frequently asked

Isn't changing the SSH port still worth it?
For a quieter auth log, sure — do it if the noise bothers you. Just don't count it as protection. It stops broad automated scanners that only probe port 22; it does nothing against anyone actually targeting you, and it's no substitute for disabling password login.
Do I still need fail2ban if I've disabled passwords?
Not for security, really. With key-only auth the banned IPs couldn't have logged in anyway. It's useful for trimming log noise and curbing resource use from persistent scanners, so run it if you like a tidy log — just don't treat it as your primary defense.
Is a passphrase on my SSH key necessary?
Yes, for laptops and anything that might be lost or stolen. The passphrase encrypts the private key at rest, so a stolen laptop doesn't hand over server access. Use ssh-agent so you type it once per session rather than every login.
What's the single most important step if I only do one thing?
Set PasswordAuthentication no after confirming your key works. That one line turns the entire wall of brute-force bot traffic into harmless noise, because they're attacking a login method that no longer exists.
Should I disable SSH entirely and use only the VPN?
Not disable SSH — bind it to the VPN interface so it's only reachable once you're on the WireGuard tunnel. You still use SSH, you've just removed it from the public internet. It's the strongest move on this page and it makes most of the other tweaks moot.

Keep reading