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.
![Server Security Hardening Checklist: 8 Steps [Solved]](/images/blog/server-security-hardening-checklist-8-steps-solved.jpg)