Skip to content
Back to Blog
Security11 min read

Server Security Hardening Checklist: 8 Steps for 2026

Lock down your Linux server with this practical checklist covering SSH configuration, firewall rules, automatic updates, intrusion detection, and audit logging.

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

Most breached servers share the same preventable gaps: default SSH ports, no firewall, outdated packages, and zero visibility into what happened during the attack. I've seen production boxes compromised in under two hours because someone skipped basic hardening. You can close those gaps in an afternoon.

This checklist walks through eight server security hardening steps that stop the majority of automated attacks and give you the visibility to catch targeted ones. Every step includes the actual commands and config snippets you need.

1. Lock down SSH access

SSH is your primary remote access vector. Securing it stops brute-force bots and credential stuffing.

Disable root login and password authentication entirely. Use SSH keys only:

sudo nano /etc/ssh/sshd_config

Set these directives:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no

Restart SSH:

sudo systemctl restart sshd

Before you restart, confirm you have a working SSH key pair and can authenticate with it. Lock yourself out and you'll need console access to recover.

Change the default port from 22 to something high and non-standard. Yes, security through obscurity isn't real security, but it drops automated scan traffic by 90%. Add this line:

Port 4822

Pick any unused port above 1024. Update your firewall rules to allow the new port before restarting SSH, or you'll lose access.

Limit SSH access by IP or subnet if your team connects from known ranges:

AllowUsers [email protected]/24

For distributed teams, implement two-factor authentication with PAM modules or use a bastion host. In support tickets I handled, most SSH compromises started with weak passwords on shared hosting accounts that also had shell access.

2. Configure a host firewall

A firewall controls what traffic reaches your services. Default-allow configurations are asking for trouble.

Use ufw on Ubuntu/Debian or firewalld on CentOS/RHEL. Here's the ufw approach:

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 4822/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw enable

Check the status:

sudo ufw status verbose

Only open ports you're actively using. A common mistake is allowing port ranges (like 20-21 for FTP) and forgetting about them. Passive FTP requires a random high port range, which punches holes in your firewall—use SFTP instead.

For database servers, bind to localhost and use SSH tunneling for remote access:

ssh -L 3306:localhost:3306 user@server -p 4822

This keeps MySQL off the public internet entirely.

If you run multiple services, consider application-level rules with fail2ban (covered in step 5). The firewall is your first line; fail2ban is your reactive layer.

3. Enable automatic security updates

Unpatched CVEs are low-hanging fruit for attackers. Automate this.

On Ubuntu/Debian, install unattended-upgrades:

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

Edit /etc/apt/apt.conf.d/50unattended-upgrades to enable security updates:

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

On RHEL/CentOS, use dnf-automatic:

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

Configure /etc/dnf/automatic.conf:

[commands]
apply_updates = yes

Automatic updates carry a small risk of breaking things. Monitor your logs and test updates on a staging server first if you're running custom applications. For most standard LAMP stacks or hosting environments, automatic security patches are safer than running months behind.

Manually update immediately when a critical vulnerability drops—don't wait for the cron job.

4. Harden kernel parameters with sysctl

The Linux kernel exposes hundreds of tunables that affect network security. A few key changes block common attack patterns.

Edit /etc/sysctl.conf or create /etc/sysctl.d/99-security.conf:

# IP forwarding (disable unless you're routing)
net.ipv4.ip_forward = 0

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

# Ignore send redirects
net.ipv4.conf.all.send_redirects = 0

# Ignore ICMP pings
net.ipv4.icmp_echo_ignore_all = 1

# Enable SYN cookies (SYN flood protection)
net.ipv4.tcp_syncookies = 1

# Log martians (packets with impossible source addresses)
net.ipv4.conf.all.log_martians = 1

# Ignore broadcast pings
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Protect against TCP time-wait assassination
net.ipv4.tcp_rfc1337 = 1

Apply the changes:

sudo sysctl -p

Disabling ICMP pings will break some monitoring tools. If you rely on ping checks, leave icmp_echo_ignore_all at 0 and use firewall rules to limit who can ping instead.

These settings won't stop every attack, but they raise the difficulty floor and reduce your attack surface for network-level exploits.

5. Deploy intrusion detection with Fail2Ban

Fail2Ban watches log files for patterns (repeated auth failures, exploit attempts) and temporarily bans the offending IP by adding firewall rules.

Install it:

sudo apt install fail2ban

Create /etc/fail2ban/jail.local:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5

[sshd]
enabled = true
port = 4822
logpath = /var/log/auth.log

Restart the service:

sudo systemctl restart fail2ban

Check banned IPs:

sudo fail2ban-client status sshd

Add jails for any public-facing service: Apache, Nginx, Postfix. Example for Nginx:

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3

Fail2Ban is reactive, not proactive. It blocks IPs after they've already started probing. Combine it with firewall rules that whitelist known good IPs for critical services.

In high-traffic environments, tune findtime and maxretry to avoid false positives from legitimate users who mistype passwords.

6. Enable comprehensive audit logging with auditd

You can't investigate what you didn't log. auditd records every syscall, file access, and command execution at the kernel level.

Install auditd:

sudo apt install auditd audispd-plugins

Start it:

sudo systemctl enable --now auditd

Add rules to watch sensitive files and directories. Edit /etc/audit/rules.d/audit.rules:

-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /var/log/auth.log -p wa -k auth_log_changes
-w /bin/su -p x -k su_execution
-w /usr/bin/sudo -p x -k sudo_execution

Reload the rules:

sudo augenrules --load

Search audit logs:

sudo ausearch -k passwd_changes

Generate reports:

sudo aureport -au

auditd logs are verbose. On busy servers they'll grow fast. Rotate them with logrotate and ship them off-server to a centralized logging system (syslog-ng, Elasticsearch, or even an S3 bucket). If an attacker gets root, the first thing they'll do is erase local logs.

7. Disable unused services and remove orphaned packages

Every running service is potential attack surface. Most default OS installs ship with services you'll never use.

List active services:

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

Disable anything you don't recognize or need:

sudo systemctl disable --now <service-name>

Common candidates: avahi-daemon, cups, bluetooth, ModemManager.

Remove orphaned or unused packages:

sudo apt autoremove
sudo apt autoclean

On CentOS/RHEL:

sudo dnf autoremove

Review installed packages manually:

dpkg --get-selections | grep -v deinstall

Look for development tools, compilers, or interpreters you don't need in production. An attacker who gets a shell will use gcc or python to compile exploits or download additional payloads. Remove them if you're not actively developing on the server.

8. Implement file integrity monitoring

File integrity monitoring (FIM) alerts you when critical system files change unexpectedly. AIDE (Advanced Intrusion Detection Environment) is the standard tool.

Install AIDE:

sudo apt install aide

Initialize the database:

sudo aideinit

This creates /var/lib/aide/aide.db.new. Move it into place:

sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

Run a check:

sudo aide --check

Schedule daily checks with cron:

sudo crontab -e

Add:

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

After legitimate system changes (package updates, config edits), update the baseline:

sudo aide --update
sudo mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db

AIDE catches rootkits and backdoors that modify binaries or config files. It won't stop an attack in progress, but it'll tell you exactly what changed during an incident.

What real attacks look like on unhardened servers

The attacks I've seen in production follow predictable patterns.

Bots scan the entire IPv4 space on port 22. If they find SSH, they try default credentials (root/root, admin/admin) and common passwords from breach lists. Weak passwords fall in minutes.

Once inside, attackers escalate to root through kernel exploits or misconfigured sudo rules, install a cryptominer or botnet agent, then cover their tracks by clearing logs. On an unhardened server with no AIDE or auditd, you won't notice until CPU spikes or your hosting provider emails you about abuse complaints.

Web application exploits are the other common entry point. An outdated WordPress plugin gives them a shell, then they pivot to the host OS. Keeping applications patched is just as important as OS hardening.

Checklist: apply these eight steps in order

  1. SSH lockdown: disable root login, use keys only, change port
  2. Firewall rules: default-deny incoming, allow only required ports
  3. Automatic updates: enable unattended security patches
  4. Kernel hardening: apply sysctl network security tunables
  5. Intrusion detection: install Fail2Ban with service-specific jails
  6. Audit logging: enable auditd and watch critical files
  7. Service reduction: disable unused daemons, remove unnecessary packages
  8. File integrity monitoring: set up AIDE with daily checks

Run through this list on every new server before putting it into production. For existing servers, implement them one at a time and test after each step.

FAQ: common hardening questions

Q: Will automatic updates break my applications?
Security-only updates rarely break things. Test on staging first if you're worried. Running months behind on patches is riskier than a rare compatibility issue.

Q: Should I change my SSH port if I'm using keys?
Yes. It doesn't add real security, but it cuts log noise from bots by 90% and reduces the load on Fail2Ban.

Q: How often should I review audit logs?
Daily, even if automated. Set up alerts for specific events (sudo usage, failed auth, file changes) so you're not drowning in data.

Q: Do I need all eight steps on a low-traffic site?
Yes. Attackers scan everything. A personal blog on a VPS gets the same bot traffic as a corporate server.

Q: What about web application firewalls or cloud-based DDoS protection?
Those are additional layers. This checklist focuses on host-level hardening. WAFs and DDoS mitigation sit in front of your server and handle different threats.

Start with SSH and firewall, then add layers

If you only do two things, lock down SSH and enable the firewall. Those alone stop the majority of opportunistic attacks.

The rest of the checklist adds defense in depth: automatic patching keeps your software current, intrusion detection reacts to active threats, and audit logging gives you forensics when something goes wrong. File integrity monitoring is your canary—if it alerts, you know someone got in.

Work through the list methodically. Each step takes 10-20 minutes. By the end of the afternoon, your server will be harder to compromise than 90% of what's on the internet.