← All posts

A Practical Linux Server Hardening Checklist for Small Teams

Aswad Gul · 2026-07-31 · 6 min read

Most hardening guides are enormous and assume a security team. This one is ordered by value per minute, for teams where the person securing the server also has other jobs.

Key Takeaways

  • Order by value per minute. Patching, SSH keys, and default-deny inbound remove most of the realistic risk in under an hour.
  • Every listening service is attack surface. If you cannot explain why it is there, that is the finding.
  • An untested backup is a hope. Restore once before you need to.
  • Documenting what normal looks like is a security control, because you cannot recognise abnormal without it.
  • Hardening is point-in-time and servers drift. The ongoing part is the part that gets skipped, and it is the part that matters most over a year.

Table of Contents

First hour

Patch everything. Unpatched known vulnerabilities remain the most common way in.

sudo apt update && sudo apt upgrade   # Debian/Ubuntu
sudo dnf upgrade --refresh            # RHEL family

Enable automatic security updates. Manual patching does not survive a busy month.

sudo apt install unattended-upgrades -y && sudo dpkg-reconfigure -plow unattended-upgrades

Security-only updates are the sensible default here. Enabling unattended upgrades for everything on a production server occasionally restarts something at an inconvenient moment.

Lock down SSH. Keys only, no root login, explicit user allowlist.

Turn on a firewall with a default-deny inbound policy.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw enable

Close what you are not using.

sudo ss -tulpn | grep LISTEN

Every listener is attack surface. If you cannot explain why it is there, investigate it.

The specific thing to look for is the address a service is bound to. A database on 127.0.0.1 is fine; the same database on 0.0.0.0 is reachable from the internet, and that single distinction is behind a large share of real compromises. Redis, Elasticsearch, MongoDB, and Docker's API have all shipped in configurations that bind broadly, and each has been exploited at scale as a result.

First day

Create non-root users with sudo, one per human. Shared accounts destroy accountability.

Install fail2ban.

sudo apt install fail2ban -y

Set up log retention long enough to investigate something you notice a month late.

Send them off the server as well as keeping them locally. Logs on the machine are only as trustworthy as the machine, and an attacker with root can edit them, which is precisely when you need them.

Configure off-server backups and test a restore. An untested backup is a hope, not a backup.

Restore into a scratch environment and confirm the data is actually usable. Also confirm the backup destination is not writable with credentials stored on the server being backed up, or ransomware takes both at once.

First week

Enable auditd for a record of privileged activity.

Set up file integrity monitoring on /etc, system binaries, and your web root.

Document what normal looks like - expected processes, listeners, users, cron jobs. You cannot spot abnormal without this.

Capture it as a file rather than as knowledge in someone's head:

{
  echo "== listeners"; ss -tulpn
  echo "== processes"; ps -eo user,comm --sort=comm | uniq
  echo "== suid"; find / -xdev -perm -4000 -type f 2>/dev/null
  echo "== cron"; ls -la /etc/cron.d/ /etc/cron.*/
  echo "== users"; awk -F: '$3 >= 1000 || $3 == 0 {print $1, $3}' /etc/passwd
} > ~/baseline-$(hostname)-$(date +%F).txt

Do this while the server is known-good, and store it off the machine. A baseline captured after a compromise records the intrusion as normal, which quietly defeats the point.

Review user accounts and keys. Old contractor access is a classic finding.

What this list deliberately omits

Several things that appear on longer guides are left out here because the value per minute is poor for a small team.

Kernel hardening parameters and mandatory access control. SELinux and AppArmor are genuinely valuable and genuinely time-consuming to configure without breaking an application. If you have the time, do it. If you have an hour, the items above matter more.

Intrusion detection tuning. Deploying a detection system you will not maintain produces a false sense of coverage. Better to have three controls that work than nine that have silently stopped.

Exhaustive CIS benchmark alignment. Useful when a framework requires it. As a starting point for a team of three, it produces a long list of low-consequence changes that consumes the attention the high-value items needed.

The omission that matters most: nothing here defends against an application vulnerability. A hardened server running a vulnerable web application still gets compromised through it, and the attacker still leaves a webshell in your web root. Hardening the host is necessary and not sufficient.

Ongoing, and this is the part that gets skipped

Hardening is a point-in-time action; servers drift. Packages update, someone opens a port for a demo, a config gets changed during an incident and never reverted.

What matters over time is noticing change: has a config file changed, is there a new listener, a new user, an unexpected outbound connection, a package with a newly published CVE.

Doing that manually across even a handful of servers does not survive a busy quarter. It has to be automated, and the automation has to be quiet enough that people keep paying attention to it.

That last clause is the whole difficulty. A monitoring system producing more findings than anyone reads has an effective true positive rate of zero regardless of how good its detections are, which is the constraint set out in why manual server monitoring fails at 3am.

That is the problem SecAI is built for: continuous monitoring of exactly these signals, with an AI pipeline that reviews findings so you get a short list of things that matter rather than a firehose. See it alongside the rest of the detection stack. The category-level view is in the guide to Linux server security software.

Frequently Asked Questions

Is this checklist enough on its own? It removes most of the realistic risk for a small team, but it is point-in-time. Without something watching for drift afterwards, its value decays over the following months.

Should I enable automatic updates on production? Security-only updates, yes, on most servers. Blanket automatic upgrades of everything carries its own outage risk and is worth more care.

Do I need SELinux or AppArmor? Valuable, and expensive in time to configure without breaking things. Do the items above first; treat mandatory access control as the next tier once they are solid.

What is the single highest-value change? Key-only SSH authentication. It removes brute force as a category rather than mitigating it.

How do I know if a listening service should be there? Trace it back to something you deliberately installed. If nobody can explain it, treat it as a finding and investigate before assuming it is benign, the sweep in how to tell if your Linux server has been compromised covers what to check.

SecAI monitors Linux servers for exactly these threats automatically.