Linux Server Security Checklist (10 Things to Do Before You Expose Anything)

Most compromised home servers aren't hit by anything clever. They're hit by a password login left on, a port forwarded "just for now", or an app that never got updated. This list is the whole defence. Each item links back to the tutorial that shows how; none of them takes more than fifteen minutes.
The checklist
| # | Do this | How to check |
|---|---|---|
| 1 | SSH keys only, passwords off | grep PasswordAuthentication /etc/ssh/sshd_config → no |
| 2 | Don't log in as root | sudo grep PermitRootLogin /etc/ssh/sshd_config → no |
| 3 | Firewall on, only needed ports | sudo ufw status |
| 4 | Fail2ban running | sudo fail2ban-client status sshd |
| 5 | Automatic security updates | /var/log/unattended-upgrades/ has recent entries |
| 6 | Nothing forwarded except the VPN | Router port-forward list |
| 7 | Apps bound to localhost behind Nginx | sudo ss -tlnp shows 127.0.0.1: for app ports |
| 8 | Backups run and have been restored once | /var/log/backup.log |
| 9 | Monitoring with alerts | Uptime Kuma sends to your phone |
| 10 | Monthly ten-minute review | Calendar reminder |
1. Keys, not passwords (tutorial 3)
Password guessing is the number-one attack. Keys make it impossible. Test from a second window before you turn passwords off.
2. No root login (tutorial 4)
In /etc/ssh/sshd_config, PermitRootLogin no. Everything goes through a named user and sudo, which is logged.
3. Firewall (tutorial 6)
ufw default deny, then allow SSH and only the ports you actually serve. Run sudo ufw status numbered and ask of each line: do I still need this?
4. Fail2ban (tutorial 7)
Even with keys, Fail2ban stops bots filling your logs and probes against any other service you expose — Nginx, Nextcloud, WordPress logins.
5. Updates (tutorials 5 and 36)
Unattended security updates with a 4 am reboot. Docker images are not covered — docker compose pull weekly, or Watchtower.
6. VPN instead of port forwards (tutorial 33)
Every forwarded port is a door onto the internet. Forward one UDP port for WireGuard and reach everything else through the tunnel. The exceptions are things that genuinely must be public — a website on 80/443 — and those sit behind Nginx with HTTPS.
7. Apps behind the proxy (tutorial 28)
Bind containers to 127.0.0.1 so the only way in is through Nginx. Docker otherwise punches straight through UFW.
8. Backups you've tested (tutorial 23)
Ransomware, a failed drive and a typo all have the same fix. Nightly, to a different disk, with one copy off-site, and you've restored a file from it at least once.
9. Monitoring (tutorial 35)
You can't fix what you don't know is broken. A Telegram message when a service drops, and a certificate-expiry warning.
10. The monthly review
Put ten minutes in the calendar:
sudo ufw status numbered
sudo fail2ban-client status sshd
last -n 20
sudo journalctl -p err -b --no-pager | tail
df -h
docker ps
tail /var/log/backup.log
Look for ports you no longer need, logins you don't recognise, errors, a filling disk, containers you forgot about, and the word "ok" in the backup log.
Things that look like security but aren't worth it
- Changing the SSH port. Scanners find it in seconds. Keys and Fail2ban do the real work; moving the port just quietens the log.
- Disabling ping. Breaks your own monitoring for no gain.
- Complex password rules on a server that doesn't accept passwords.
- Antivirus. On a Linux server with no users opening email, it mainly consumes CPU.
Troubleshooting
You've locked yourself out while hardening. Recovery mode (tutorial 37) at the keyboard, or the cloud provider's console.
Fail2ban keeps banning your own IP. Add your home network to ignoreip.
A public app needs a port you'd rather not open. Put it behind the VPN and ask whether it really needs to be public. Usually it doesn't.
Wrapping up
Keys, no root, firewall, Fail2ban, auto-updates, one VPN port, apps behind Nginx, tested backups, alerts, and a monthly look. Nothing on that list is clever, which is exactly why it works.
