A freshly installed Linux server is not a secure Linux server. Default configurations are built for convenience, not protection. Root SSH login is usually enabled. Unused services run on open ports. Kernel parameters are tuned for compatibility rather than security. This guide covers Linux server hardening step by step, with every command you need to lock down a production server running Ubuntu, Debian, RHEL, or AlmaLinux in 2026.

Step 1: Keep the System Updated
Unpatched software is the largest attack surface on most servers. Start here before anything else. On Ubuntu and Debian:
apt update && apt upgrade -y
apt install unattended-upgrades -y
dpkg-reconfigure --priority=low unattended-upgrades
On RHEL, AlmaLinux, and Rocky Linux:
dnf upgrade -y
dnf install dnf-automatic -y
systemctl enable --now dnf-automatic.timer
Automatic updates apply security patches without manual intervention. Confirm security updates are enabled in /etc/apt/apt.conf.d/50unattended-upgrades on Debian-based systems.
Step 2: Harden SSH Access

SSH is the most targeted service on any public server. Edit /etc/ssh/sshd_config and apply these settings:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
LoginGraceTime 20
AllowUsers yourusername
Test and restart SSH after saving:
sshd -t && systemctl restart sshd
Set up key-based auth before disabling passwords. Without a key in place first, removing password auth will lock you out. Consider moving SSH off port 22 to reduce scanner noise:
# Add to sshd_config then open new port in firewall first
Port 2222
Step 3: Configure the Firewall
Block all incoming traffic by default and open only the ports you actually need. On Ubuntu and Debian, UFW works well:
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verbose
On RHEL, AlmaLinux, and Rocky Linux, use firewalld:
firewall-cmd --set-default-zone=drop
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
For each open port, confirm which service needs it. Avoid opening wide port ranges. Each open port is an attack surface.
Step 4: Install fail2ban
Fail2ban watches your log files and bans IPs that show brute force behaviour. Install it first:
apt install fail2ban -y # Ubuntu/Debian
dnf install fail2ban -y # RHEL/AlmaLinux
Create a local config file so updates do not overwrite your settings:
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
EOF
systemctl enable --now fail2ban
fail2ban-client status sshd
Check the banned IP count after 24 hours. The number is usually higher than you expect. Automated scanners probe every server on the internet around the clock.
Step 5: Disable Unused Services
Every running service is a potential attack path. List what starts at boot and turn off anything you do not recognise or need:
systemctl list-unit-files --state=enabled
systemctl disable --now bluetooth
systemctl disable --now avahi-daemon
systemctl disable --now cups
systemctl disable --now rpcbind
ss -tlnp
For each port shown by ss -tlnp, either confirm you need it or stop the service responsible for it.
Step 6: Harden the Kernel with sysctl

Kernel parameters control low-level networking and process behaviour. These sysctl settings are a core part of Linux server hardening and close several common attack vectors. Create a dedicated config file for persistence:
cat > /etc/sysctl.d/99-hardening.conf << 'EOF'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.tcp_syncookies = 1
net.ipv6.conf.all.disable_ipv6 = 1
fs.suid_dumpable = 0
kernel.randomize_va_space = 2
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
EOF
sysctl -p /etc/sysctl.d/99-hardening.conf
These settings apply immediately. They prevent IP spoofing, block ICMP redirects, enable SYN flood protection, and restrict access to kernel internals from unprivileged users.
Step 7: Enable System Auditing with auditd
Auditd records file access and user actions. It is essential for forensics after an incident and required by PCI DSS and HIPAA. Install and enable it:
apt install auditd -y # Ubuntu/Debian
dnf install audit -y # RHEL/AlmaLinux
systemctl enable --now auditd
Add rules to watch critical files and commands:
cat >> /etc/audit/rules.d/hardening.rules << 'EOF'
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/ssh/sshd_config -p wa -k sshd
-w /usr/bin/sudo -p x -k sudo_usage
-w /etc/cron.d -p wa -k cron
EOF
augenrules --load
ausearch -k identity --start today
Step 8: User and Permission Hygiene
Check for accounts with empty passwords and any users holding UID 0:
awk -F: '($2 == "") {print $1}' /etc/shadow
awk -F: '($3 == 0) {print $1}' /etc/passwd
usermod -L unused_account
chage -M 90 -W 14 username
Also tighten permissions on sensitive files:
chmod 600 /etc/ssh/sshd_config
chmod 600 /etc/shadow
chmod 644 /etc/passwd
chmod 700 /root
Step 9: Audit with Lynis
Lynis is an open-source security scanner that checks your system and produces a prioritised findings list. Run it after finishing the steps above:
apt install lynis -y
lynis audit system
A fresh install typically scores 55 to 60. After following this guide, most systems reach above 80. Work through the suggestions section to close remaining gaps. For compliance-driven environments, also download the CIS Benchmarks for your distribution. They are free with registration and the industry standard for auditable hardening.
Step 10: Monitor Your Log Files
Log monitoring lets you catch suspicious activity quickly. Start by watching failed SSH login attempts:
tail -f /var/log/auth.log | grep "Failed password"
tail -f /var/log/secure | grep "Failed password"
grep "Failed password" /var/log/auth.log | grep "$(date '+%b %e')" | wc -l
For a fuller picture, forward logs to a centralised syslog server. An attacker who gains access can modify local logs. Remote logs cannot be tampered with from the compromised machine.
Conclusion
Good Linux server hardening is not a one-time task. Threat patterns change, software gets patched, and new vulnerabilities appear regularly. Run Lynis every quarter and review your auth logs weekly. Start with the four highest-impact steps: update packages, harden SSH, configure the firewall, and install fail2ban. Those four block the vast majority of automated attacks on their own. Then work through the rest of the checklist. Since kernel vulnerabilities are a common hardening gap, also read our guide on patching the Copy Fail CVE-2026-31431 Linux kernel vulnerability. And if a disk-full event has ever taken down your database, check our post on recovering MySQL after a disk full crash so you are ready for that scenario too.