My Real-World SSH Hardening Notes for Ubuntu VPS (Junior Sysadmin Edition)
Copy-paste friendly SSH security checklist I use on fresh VPS. Beginner-friendly, real sysadmin notes.
Updated Dec 2025: Download the FREE printable PDF checklist here ↓
https://gosaid.gumroad.com/l/ssh-said
I’m a junior sysadmin learning in public. I manage a handful of Ubuntu servers on regular VPS providers. No bastion hosts, no IAM, no corporate magic. Just plain boxes exposed to the internet.
When you run that kind of setup, bots start knocking on your SSH port within minutes. The goal isn’t “perfect security.” The goal is simple, repeatable basics that make your box boring to attack and easy to maintain.
Below is the baseline I apply to every new Ubuntu/debian server. Each section has:
Why (the real world reason)
Steps (copy-paste friendly)
Checks (quick sanity tests so you don’t lock yourself out)
1) Disable Password Login
Why: Passwords are guessable and phishable. If SSH won’t accept passwords at all, brute force attempts become noise in the logs, not a risk.
Steps:
Open the SSH daemon config:
sudo nano /etc/ssh/sshd_configSet these (ensure they’re not commented with #):
PasswordAuthentication no ChallengeResponseAuthentication no UsePAM noValidate config & reload:
sudo sshd -t # no output = good sudo systemctl reload ssh || sudo systemctl restart ssh
Checks:
From your laptop, force password method to confirm it’s blocked:
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no user@serverYou should not be able to get in.
2) Use SSH Keys Only
Why: Keys are asymmetric, long, and not brute-forceable in practice. They’re also easy to rotate per machine/client.
Steps (local → server):
If you don’t have a key yet (Ed25519 is a solid default):
ssh-keygen -t ed25519 -C “key_for_server1” -f ~/.ssh/id_ed25519_server1Copy your public key to the server:
ssh-copy-id user@server # or manually: # on server: # mkdir -p ~/.ssh && chmod 700 ~/.ssh # nano ~/.ssh/authorized_keys # chmod 600 ~/.ssh/authorized_keys
Checks:
Log in normally:
ssh user@serverIf it works, great. If it fails, don’t log out of any existing root/session until fixed.
3) Disable Root SSH Login
Why: Reduces attack surface. Even if someone knew (or guessed) a root credential, SSH won’t admit root sessions. You’ll escalate with sudo when needed.
Steps:
Edit sshd config:
sudo nano /etc/ssh/sshd_configEnsure:
PermitRootLogin noValidate & reload:
sudo sshd -t sudo systemctl reload ssh
Checks:
Try:
ssh root@server
You should be denied.
4) Allow Specific Users
Why: Whitelisting known users is a cheap, effective guardrail. Even if someone creates a stray account, SSH won’t admit it.
Steps:
In sshd_config:
AllowUsers youruser(Optional: prefer groups)
# Add a group and restrict by group # sudo addgroup sshusers # sudo usermod -aG sshusers youruser # And in sshd_config: # AllowGroups sshusersValidate & reload:
sudo sshd -t sudo systemctl reload ssh
Checks:
Confirm your user still logs in:
ssh youruser@serverTry with a non-allowed user (should fail).
5) Firewall: Open Only What You Need
Why: A simple host firewall blocks random noise and mistakes. On a small VPS, UFW keeps it straightforward.
Steps:
# Allow SSH first or you’ll lock yourself out
sudo ufw allow OpenSSH
# Typical web stack
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable firewall
sudo ufw enable
# Verify
sudo ufw status verboseIf you changed SSH port later:
sudo ufw allow 44220/tcp
# change with your own ssh portChecks:
Confirm you can still SSH after enabling UFW.
Confirm ports other than the allowed ones are blocked:
sudo ufw status numbered
6) Fail2ban
Why: Lightweight, effective. It watches auth logs and bans IPs that fail repeatedly. It’s not hype; it just works.
Steps:
sudo apt update
sudo apt install fail2ban -yCreate a local jail:
sudo nano /etc/fail2ban/jail.localRecommended minimal:
[sshd]
enabled = true
maxretry = 3
bantime = 1h
findtime = 10mStart & enable:
sudo systemctl enable --now fail2ban
sudo systemctl status fail2banChecks:
Tail the log and observe bans over time:
sudo tail -f /var/log/fail2ban.log
7) Limit MaxAuthTries
Why: Caps how many authentication attempts SSH will allow per connection. It slows down brute-force scripts and reduces log spam.
Steps:
In sshd_config:
MaxAuthTries 3Validate & reload:
sudo sshd -t sudo systemctl reload ssh
Checks:
From a throwaway client, intentionally fail a few times and watch:
ssh -o PubkeyAuthentication=no user@server
# enter wrong passwords; it should cut you off quickly8) Short LoginGraceTime
Why: Limits how long SSH keeps a half-open session during login. Cuts off slow or scripted probing that stalls. 30 seconds is a good default.
Steps:
In sshd_config:
LoginGraceTime 30Validate & reload:
sudo sshd -t sudo systemctl reload ssh
Checks:
Try connecting and waiting at the password prompt (with passwords disabled you’ll just see it time out faster under certain flows).
The point is: SSH won’t babysit idle, half-auth sessions.
Order of Operations (So You Don’t Lock Yourself Out)
Create user + add SSH key for that user.
Test key login in a new terminal (keep the original root session open).
Apply PasswordAuthentication no and PermitRootLogin no.
Add AllowUsers youruser.
Reload SSH and test again.
Enable UFW with SSH allowed.
Install Fail2ban.
Tune MaxAuthTries and LoginGraceTime.
Validate each change with sshd -t and a fresh SSH attempt.
Bonus sanity command:
# Show which sshd binary is used and version (helpful for debugging)
sshd -V 2>&1Quick Rollback Tips (When You Mess Up)
Most cloud panels offer a web/VNC console. Use it to revert
/etc/ssh/sshd_config.Keep a second terminal logged in as safety before reloading SSH.
If UFW locked you out: sudo ufw disable (from console), fix rules, re-enable.
Validate config before reload:
sudo sshd -t
Closing Thoughts
This isn’t a security thesis. It’s a calm, repeatable routine for regular Ubuntu VPS on the public internet. With just these basics:
SSH won’t accept passwords.
Only your user is allowed.
Firewall is tight.
Fail2ban kicks out noisy IPs.
SSH won’t entertain endless guesses or slow probes.
Nothing fancy. But it’s the difference between “constantly worried” and “reasonably protected.”
I’m still learning. The stack evolves. But doing the right basics consistently beats chasing shiny tricks.
No guru here. Just notes from a junior sysadmin running real servers.
If you need help setting up your server, generating SSH keys, or maintaining WordPress, feel free to message me.


This piece really got me thinking! Such solid SSH basics are vital even for my home AI projects. Really insightsful.