Skip to content
Back to Blog
Security11 min read

Server Security Hardening Checklist: 8 Steps [Solved]

Lock down your Linux server in one session with this practical hardening sequence—from SSH lockdown to intrusion detection—that sysadmins can execute today.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Security Hardening Checklist: 8 Steps [Solved]
On this page

A freshly deployed server is an open door. I've seen production VPS instances compromised within hours of going live because the sysadmin skipped basic hardening. The good news? You can lock down the most common attack vectors in a single focused session.

This checklist walks you through eight hardening steps in sequence, each building on the last. You'll patch the OS, configure a firewall, lock down SSH, install fail2ban, disable unused services, set up automatic updates, configure logging, and deploy intrusion detection. Real commands, no fluff.

Step 1: Patch the operating system

Start here. Always.

Outdated packages are the easiest entry point for attackers. Before you configure anything else, bring the system current. On Debian-based distributions:

apt update
apt upgrade -y
apt dist-upgrade -y
apt autoremove -y

For RHEL-based systems:

yum update -y
# or on newer versions
dnf upgrade -y

Reboot if the kernel was updated:

reboot

Check your current kernel version after the reboot:

uname -r

In support tickets I handled, the usual culprit was a server running a kernel from six months ago with known privilege escalation bugs. Patching first means you're not hardening a system that's already vulnerable at the kernel level.

Step 2: Configure the firewall

A firewall blocks everything except what you explicitly allow. Default-deny is the goal.

Most modern Linux distributions ship with ufw (Uncomplicated Firewall) or firewalld. I prefer ufw for its simplicity on Ubuntu and Debian systems. Install it if it's missing:

apt install ufw -y

Set default policies—deny incoming, allow outgoing:

ufw default deny incoming
ufw default allow outgoing

Now allow SSH before you enable the firewall, or you'll lock yourself out:

ufw allow 22/tcp

If you're running a web server, allow HTTP and HTTPS:

ufw allow 80/tcp
ufw allow 443/tcp

For mail servers, open the necessary ports:

ufw allow 25/tcp
ufw allow 587/tcp
ufw allow 993/tcp

Enable the firewall:

ufw enable

Verify the rules:

ufw status verbose

On RHEL-based systems using firewalld, the equivalent sequence is:

firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
firewall-cmd --list-all

Only open ports you're actively using. Every open port is a potential entry point.

Step 3: Lock down SSH access

SSH is how you manage the server remotely. It's also how attackers try to get in.

Edit the SSH daemon configuration:

nano /etc/ssh/sshd_config

Make these changes:

Port 2222
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
UsePAM yes
X11Forwarding no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2

Change the default port. Moving SSH off port 22 stops almost all automated brute-force scans. Pick a high port number above 1024.

Disable root login. Force users to log in with a regular account and then escalate with sudo. This creates an audit trail.

Disable password authentication. Use SSH keys only. If you haven't already set up key-based authentication, do it before you disable passwords. On your local machine:

ssh-keygen -t ed25519 -C "[email protected]"
ssh-copy-id -p 2222 your_username@server_ip

Test the key login before you restart SSH. Open a second terminal session and try to connect with your key. If it works, restart the SSH daemon:

systemctl restart sshd

Update your firewall rule to match the new port:

ufw delete allow 22/tcp
ufw allow 2222/tcp

Now brute-force bots scanning port 22 will find nothing.

Step 4: Install and configure fail2ban

Even with SSH hardened, persistent attackers will keep trying. Fail2ban watches your logs and bans IPs after repeated failed login attempts.

Install fail2ban:

apt install fail2ban -y

Create a local configuration file:

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
nano /etc/fail2ban/jail.local

Edit the [sshd] section:

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

This bans an IP for one hour after three failed login attempts within ten minutes. Adjust bantime and maxretry based on your tolerance for false positives.

Restart fail2ban:

systemctl restart fail2ban
systemctl enable fail2ban

Check the status:

fail2ban-client status sshd

You'll see a list of currently banned IPs. In my experience, fail2ban stops 95% of brute-force attempts cold.

Step 5: Disable unused services and remove unnecessary packages

Every running service is a potential attack surface.

List all active services:

systemctl list-units --type=service --state=running

Disable anything you don't need. Common candidates:

systemctl disable avahi-daemon
systemctl stop avahi-daemon
systemctl disable cups
systemctl stop cups

Remove packages you're not using:

apt autoremove -y
apt purge <package_name>

Be careful here—don't remove dependencies your running services need. Check what a package does before purging it:

apt-cache show <package_name>

A minimal installation is easier to defend. If you're not running a print server, you don't need CUPS.

Step 6: Enable automatic security updates

Manual patching is fine for the first round, but you need automatic updates to stay ahead of new vulnerabilities.

On Debian and Ubuntu, install unattended-upgrades:

apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades

Edit the configuration:

nano /etc/apt/apt.conf.d/50unattended-upgrades

Ensure security updates are enabled:

Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
};

You can also enable automatic reboots after kernel updates:

Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";

This schedules reboots for 3 AM. Adjust to match your maintenance window.

On RHEL-based systems, enable yum-cron or dnf-automatic:

yum install yum-cron -y
systemctl enable yum-cron
systemctl start yum-cron

Edit /etc/yum/yum-cron.conf and set:

apply_updates = yes

Automatic updates won't catch everything, but they'll handle critical security patches without manual intervention.

Step 7: Configure centralized logging

Logs are your forensic evidence when something goes wrong. You need them stored somewhere an attacker can't delete.

For a single server, configure rsyslog to forward logs to a remote syslog server or a logging service. Edit /etc/rsyslog.conf:

nano /etc/rsyslog.conf

Add a forwarding rule:

*.* @@remote-syslog-server:514

Restart rsyslog:

systemctl restart rsyslog

If you don't have a remote syslog server, consider a managed logging service or at minimum configure log rotation to prevent disk fills:

nano /etc/logrotate.d/rsyslog

Set aggressive rotation:

/var/log/syslog
/var/log/auth.log
{
    rotate 14
    daily
    missingok
    notifempty
    compress
    delaycompress
    postrotate
        /usr/lib/rsyslog/rsyslog-rotate
    endscript
}

Check your current log sizes:

du -sh /var/log/*

I've responded to incidents where attackers wiped /var/log/auth.log to cover their tracks. Remote logging prevents that.

Step 8: Deploy intrusion detection with AIDE

Intrusion detection catches changes to critical system files—a sign that someone gained root access.

Install AIDE (Advanced Intrusion Detection Environment):

apt install aide -y

Initialize the database:

aideinit

This takes a few minutes. AIDE creates a baseline snapshot of your filesystem. Move the new database into place:

cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db

Run a manual check:

aide --check

If the output is clean, you're good. Schedule daily checks via cron:

crontab -e

Add:

0 2 * * * /usr/bin/aide --check | mail -s "AIDE Report" [email protected]

This emails you a daily report. Any unexpected changes to /bin, /sbin, /usr/bin, or /etc will show up immediately.

Alternatively, use rkhunter or chkrootkit for rootkit detection:

apt install rkhunter -y
rkhunter --update
rkhunter --check --skip-keypress

Both tools are useful, but AIDE gives you file integrity monitoring across the entire system.

What about ongoing maintenance?

Hardening isn't a one-time checklist. Schedule monthly reviews:

  • Check fail2ban logs for new attack patterns
  • Review firewall rules for ports you've opened temporarily
  • Verify automatic updates are running
  • Rotate and archive old logs
  • Re-run AIDE or rkhunter checks manually

Set a calendar reminder. I've seen hardened servers drift back to insecure configurations because no one revisited the settings after the initial setup.

How long does SSH key generation take?

Under a second on modern hardware. The ssh-keygen command with ed25519 is almost instant. RSA keys take slightly longer but still complete in under two seconds.

Can I run fail2ban and a firewall together?

Yes. Fail2ban adds temporary firewall rules to block IPs dynamically. It works on top of your existing firewall configuration. They complement each other.

What if I lock myself out of SSH?

Use your hosting provider's console or rescue mode to regain access. Most VPS control panels offer a web-based console that bypasses SSH entirely. Fix the SSH config, restart the daemon, and try again.

Do I need AIDE if I have fail2ban?

Yes. Fail2ban stops brute-force login attempts. AIDE detects post-compromise activity—someone modifying binaries or config files after they've already gained access. Different layers.

How often should I update the AIDE database?

After any legitimate system changes—software installations, config edits, package upgrades. Run aideinit again to create a new baseline. Otherwise, AIDE will flag your own changes as intrusions.

Will automatic updates break my applications?

Rarely, but it happens. Test updates in a staging environment first if you're running custom applications. For most hosting setups—WordPress, static sites, standard LAMP stacks—automatic security updates are safe.

Lock it down, then maintain it

You've patched the OS, configured a firewall, locked down SSH, installed fail2ban, trimmed unused services, enabled automatic updates, set up logging, and deployed intrusion detection. That's eight layers of defense you didn't have an hour ago.

The real test is maintenance. Check your logs weekly. Review fail2ban bans. Keep your firewall rules minimal. A hardened server stays hardened only if you keep an eye on it.