Skip to content
Back to Blog
Security10 min read

Server Security Hardening Checklist: 9 Steps for 2026

A practical workflow for locking down Linux servers—SSH keys, firewall rules, automatic patching, and intrusion detection—drawn from real production environments.

Written by Abdul AbrorTechnical Hosting Support Engineer
Server Security Hardening Checklist: 9 Steps for 2026
On this page

Most breached servers share the same story: default SSH port, password auth still enabled, firewall wide open, and months-old kernel vulnerabilities. The attack surface practically advertises itself.

I've rebuilt too many compromised VPS instances where a single hardening step would have stopped the intrusion. This checklist walks through nine concrete actions that block the most common attack vectors—no fluff, just the commands and config snippets you need to run today.

Step 1: Disable Root Login and Enforce SSH Keys

Password authentication over SSH is a liability. Brute-force bots hammer every publicly routable IP, trying common credentials around the clock.

First, create a non-root user with sudo privileges:

adduser deployuser
usermod -aG sudo deployuser

Generate an SSH key pair on your local machine if you haven't already:

ssh-keygen -t ed25519 -C "[email protected]"

Copy the public key to the server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub deployuser@your_server_ip

Test the key login, then edit /etc/ssh/sshd_config:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no

Restart SSH:

systemctl restart sshd

Now only your key can unlock the door. Root is locked out entirely.

Step 2: Change the Default SSH Port

Port 22 attracts automated scanners like moths to a flame. Moving SSH to a non-standard port—say, 2849 or 49222—drops the noise in your auth logs by an order of magnitude.

In /etc/ssh/sshd_config:

Port 2849

Restart SSH, then update your firewall rules (covered next) to allow the new port. Remember to test the new port before closing your current session:

ssh -p 2849 deployuser@your_server_ip

Keep the old session open until you've confirmed the new port works.

Step 3: Configure a Stateful Firewall

A firewall should deny everything by default and allow only the services you explicitly need. UFW makes this straightforward on Debian and Ubuntu systems.

Install and enable UFW:

apt update && apt install ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 2849/tcp  # your SSH port
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

For RHEL-based systems, use firewalld:

firewall-cmd --permanent --add-port=2849/tcp
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

Check the active rules:

ufw status verbose
# or
firewall-cmd --list-all

If you run a database, bind it to localhost and never expose port 3306 or 5432 to the internet. Applications can connect locally or over a VPN tunnel.

Step 4: Automate Security Updates

Manual patching sounds responsible until you forget for three months and a public exploit drops. Automatic updates for security patches keep the kernel and critical packages current without daily babysitting.

On Debian/Ubuntu, install unattended-upgrades:

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

Edit /etc/apt/apt.conf.d/50unattended-upgrades to enable automatic reboots if needed:

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

For RHEL/CentOS, enable dnf-automatic:

dnf install dnf-automatic
systemctl enable --now dnf-automatic.timer

Check /etc/dnf/automatic.conf and set apply_updates = yes under the [commands] section.

Automatic updates won't catch every edge case, but they close the window on known CVEs faster than any manual schedule.

Step 5: Install and Configure Fail2Ban

Fail2Ban watches your logs for repeated authentication failures and temporarily bans the offending IPs. It's a lightweight intrusion-prevention layer that stops brute-force attempts cold.

Install Fail2Ban:

apt install fail2ban
# or
dnf install fail2ban

Copy the default config:

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

Edit /etc/fail2ban/jail.local and enable the SSH jail:

[sshd]
enabled = true
port = 2849
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600

Restart Fail2Ban:

systemctl restart fail2ban
systemctl enable fail2ban

Check banned IPs:

fail2ban-client status sshd

In support tickets I handled, the usual culprit was a compromised WordPress admin using the same weak password everywhere. Fail2Ban bought enough time to spot the pattern and lock down the account.

Step 6: Enable AppArmor or SELinux

Mandatory access control systems confine applications to their designated file paths and system calls. Even if an attacker compromises a service, they can't pivot to other parts of the system.

Debian and Ubuntu ship with AppArmor enabled by default. Verify:

aa-status

If it's not active:

systemctl enable apparmor
systemctl start apparmor

RHEL and CentOS use SELinux. Check the mode:

getenforce

If it returns Permissive or Disabled, set it to Enforcing in /etc/selinux/config:

SELINUX=enforcing

Reboot to apply. SELinux denials show up in /var/log/audit/audit.log. Use ausearch and audit2allow to troubleshoot policies that block legitimate operations, but never disable SELinux just because an app complains—fix the policy instead.

Step 7: Harden Kernel Parameters with sysctl

The kernel exposes hundreds of tunables through /etc/sysctl.conf. A few key settings improve network security and reduce the impact of certain DoS attacks.

Add these lines to /etc/sysctl.conf:

# Ignore ICMP ping requests
net.ipv4.icmp_echo_ignore_all = 1

# Disable IP forwarding
net.ipv4.ip_forward = 0
net.ipv6.conf.all.forwarding = 0

# Enable SYN cookies
net.ipv4.tcp_syncookies = 1

# Ignore ICMP redirects
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# Log martian packets
net.ipv4.conf.all.log_martians = 1

Apply the changes:

sysctl -p

These settings won't stop a determined attacker, but they close off some low-hanging reconnaissance paths and make SYN flood attacks harder to execute.

Step 8: Deploy an Intrusion Detection System

Fail2Ban handles obvious brute-force patterns. For deeper visibility, install an IDS like AIDE or OSSEC to monitor file integrity and detect rootkits.

AIDE (Advanced Intrusion Detection Environment) creates a database of file checksums and alerts you to unexpected changes:

apt install aide
aideinit
mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

Run a check:

aide --check

Schedule daily checks via cron:

echo "0 5 * * * /usr/bin/aide --check | mail -s 'AIDE Report' [email protected]" | crontab -

OSSEC offers host-based intrusion detection with log analysis, rootkit detection, and alerting. Installation involves compiling from source or using pre-built packages, and the config lives in /var/ossec/etc/ossec.conf. Point it at your web server logs, SSH logs, and system logs to catch anomalies.

Both tools generate noise at first. Tune the rules to ignore benign changes like package updates, and focus alerts on critical paths like /etc/passwd, /etc/shadow, and web root directories.

Step 9: Implement Regular Security Audits

Hardening is not a one-time checklist. Drift happens. Services get added, firewall rules accumulate exceptions, and old user accounts linger.

Schedule a monthly audit:

  1. List all open ports: ss -tuln or netstat -tuln
  2. Review active user accounts: cut -d: -f1 /etc/passwd
  3. Check sudo privileges: cat /etc/sudoers and files in /etc/sudoers.d/
  4. Scan for world-writable files: find / -type f -perm -002 -ls 2>/dev/null
  5. Review cron jobs: crontab -l for each user and check /etc/cron.*
  6. Inspect listening services: systemctl list-units --type=service --state=running
  7. Run a vulnerability scanner: tools like Lynis automate many of these checks

Lynis is a solid open-source auditing tool:

wget -O - https://downloads.cisofy.com/lynis/lynis-3.0.9.tar.gz | tar xz
cd lynis
./lynis audit system

It produces a detailed report with a hardening index and specific recommendations.

What if the server is already live with users?

Rolling out these changes on a production server requires care. SSH changes especially can lock you out if you make a mistake.

Always test SSH config changes in a new session before closing your current one. Keep a console access method available—most VPS providers offer a web-based VNC console or serial console. Stage changes during a maintenance window and announce any brief service interruptions, especially if you're rebooting for kernel updates.

For high-traffic environments, consider blue-green deployments: harden a fresh server, migrate traffic, then decommission the old one. That way you can validate the entire stack before cutting over.

Your First 30 Minutes

Start with SSH hardening and the firewall. Those two steps block the majority of automated attacks you'll see in the wild. Add Fail2Ban next, then schedule the automatic updates.

The rest—AppArmor, sysctl tunables, IDS—can roll out over the following week. Every step tightens the perimeter. None of them require exotic tools or deep kernel knowledge, just a methodical approach and a willingness to lock things down before an incident forces your hand.

Hardening isn't glamorous, but it's the difference between a server that quietly does its job and one that ends up in your 3 a.m. incident log.

FAQ

How often should I rotate SSH keys?

Rotate keys annually or whenever an employee with key access leaves. Use separate keys per admin rather than sharing a single team key, so you can revoke access granularly.

Should I disable IPv6 if I'm not using it?

Yes, if you're not actively using IPv6, disable it in /etc/sysctl.conf with net.ipv6.conf.all.disable_ipv6 = 1. Otherwise, you're maintaining an extra attack surface with no benefit.

Can I run Fail2Ban and an IDS together?

Absolutely. Fail2Ban handles reactive banning, while an IDS like AIDE or OSSEC provides file integrity monitoring and anomaly detection. They complement each other.

What about web application firewalls?

If you're running a web app, place it behind a WAF like ModSecurity or a cloud-based option like Cloudflare. Host-level hardening secures the OS; a WAF secures the application layer.