SSH Hardening Checklist for Your First Linux Server
A practical, terminal-first checklist for locking down SSH on a new Linux server: keys, sshd_config, firewall rules, updates, and login monitoring.

You spin up a server, point a domain at it, and leave SSH on port 22 with password login enabled. Within an hour, the auth log is full of attempts from addresses you have never heard of. That is normal internet background noise, and it is also the reason the first thirty minutes of a server's life should be spent locking the front door.
None of this requires a control panel or a paid service. It requires a terminal, about twenty minutes, and the discipline to test each change before moving to the next. Here is the checklist in the order that keeps you from locking yourself out.
Step One: Create a Real User and a Key Pair
If you have been logging in as root, fix that first. Create a normal account with sudo rights and give it a key.
On your laptop, generate an Ed25519 key with a passphrase — the passphrase protects the private key if your machine is ever stolen:
ssh-keygen -t ed25519 -a 100 -C "you@laptop"
Copy the public half to the server. If the server still accepts password login, ssh-copy-id user@your-server does the job in one command. Without that option, paste the contents of the .pub file into ~/.ssh/authorized_keys on the server, then run chmod 700 ~/.ssh and chmod 600 ~/.ssh/authorized_keys. Wrong permissions here are the most common reason key login silently fails.
Now test it in a second terminal before you change any server configuration. Keep that first session open as a safety net until the very end.
Step Two: Tighten sshd_config
The SSH daemon's configuration lives at /etc/ssh/sshd_config, with drop-in overrides in /etc/ssh/sshd_config.d/ on most current distributions. Edit it with sudo, and change one group of settings at a time.
- PermitRootLogin no — root logs in only after you are already on the box, via sudo.
- PasswordAuthentication no — the single biggest win; it ends brute-force guessing entirely.
- KbdInteractiveAuthentication no — on older systems this is written as ChallengeResponseAuthentication. With PAM in the mix, leaving this at yes can keep password prompts alive even after you disable PasswordAuthentication.
- AllowUsers yourname — an explicit allowlist. Anything not listed is rejected before authentication even starts.
- MaxAuthTries 3 and LoginGraceTime 30 — fewer guesses per connection, less time wasted on half-open sessions.
- ClientAliveInterval 300 with ClientAliveCountMax 2 — drops idle sessions after roughly ten minutes.
Before restarting, validate the file: sudo sshd -t. Then reload rather than restart, so existing sessions survive: sudo systemctl reload ssh on Debian and Ubuntu, sudo systemctl reload sshd on RHEL-family systems.
What about changing the port?
Moving SSH off 22 does not make it more secure — a port scan finds it in seconds. It does cut the volume of automated noise in your logs, which some people find worth the inconvenience. If you do it, remember the firewall rule and any provider-side network rules, and update your local ~/.ssh/config so you are not typing the port every time.
Step Three: Firewall Rules That Match Reality
On Ubuntu and Debian, ufw is the quickest route. Order matters: allow SSH before enabling the firewall, or you will cut your own connection.
- sudo ufw allow OpenSSH — or sudo ufw limit 22/tcp, which throttles repeated connections from one address.
- sudo ufw default deny incoming and sudo ufw default allow outgoing.
- sudo ufw enable, then sudo ufw status verbose to confirm.
- Open additional ports only as you need them: sudo ufw allow 80,443/tcp for a web server.
On RHEL, Alma, or Rocky, firewalld takes the same role: sudo firewall-cmd --permanent --add-service=ssh, then sudo firewall-cmd --reload. If your provider offers a cloud firewall in front of the instance, keep both layers consistent — a rule that exists in only one place is a debugging session waiting to happen.
Step Four: Keep the System Patched
Hardening a server you never update is like changing the locks on a door with a broken hinge. Start with sudo apt update && sudo apt upgrade or sudo dnf upgrade, and reboot if the kernel changed.
Then automate it. On Debian and Ubuntu, install unattended-upgrades and check /etc/apt/apt.conf.d/50unattended-upgrades to confirm security updates are enabled. On RHEL-family systems, dnf-automatic does the same work. Automatic patching is not a substitute for reading what changed, but it closes the gap between disclosure and your next login.
Step Five: Watch Who Gets In
You cannot harden what you never look at. sudo journalctl -u ssh -f streams authentication events live; on older Debian systems the same data sits in /var/log/auth.log. Filters are worth learning: journalctl -u ssh --since "1 hour ago" | grep -i failed.
Two commands give you the history at a glance. last lists successful logins with source addresses and durations. sudo lastb lists failed attempts, provided /var/log/btmp exists. If the same address appears dozens of times, that is your cue.
For automatic blocking, fail2ban reads the auth log and adds temporary firewall drops after repeated failures. Install it, leave the default sshd jail enabled, and confirm it is working with sudo fail2ban-client status sshd. Pairing it with key-only authentication means very few addresses ever reach a password prompt in the first place.
One habit worth building: check last and the fail2ban status every Monday morning. An unfamiliar login line is not paranoia — it is information.
Verify Everything Before You Walk Away
Do the last checks from a fresh terminal, not the session you have been working in. Open a new window, connect with your key, and run sudo sshd -T | grep -E "passwordauth|permitrootlogin|allowusers" to see the effective configuration rather than what you think you wrote. If something is off, ssh -vvv user@server tells you exactly which authentication methods the client and server agreed on.
If you do lock yourself out, most providers offer a web console or a rescue mode — that is what it is for, and it works better when you have not also broken the firewall in the same afternoon. Make one change, test it, then make the next. Twenty minutes of patience here saves an evening of recovery, and leaves you with a server you actually understand.
Photo: Brett Sayles / Pexels



