Production servers attract automated attacks within minutes of going live. I've seen fresh VPS instances hit by brute-force login attempts before the first application even deployed. A hardened server won't stop every threat, but it raises the bar high enough to deflect opportunistic attacks and buy time when a zero-day lands.
This checklist covers the twelve core hardening steps I run on every Linux server before it handles live traffic. Most take under five minutes each. You don't need to be a security specialist to follow them—just SSH access and root or sudo privileges.
1. Disable root SSH login
The root account is every attacker's first target. Disabling direct root login over SSH forces attackers to compromise a regular user account first, then escalate privileges—two steps instead of one.
Open the SSH daemon configuration:
sudo nano /etc/ssh/sshd_config
Find the line PermitRootLogin and set it to no:
PermitRootLogin no
Restart SSH to apply:
sudo systemctl restart sshd
Before you log out, test that your regular user can still connect and use sudo. I once locked myself out by disabling root login before confirming sudo worked. Had to use the VPS console to fix it.
2. Use SSH keys instead of passwords
Password authentication is weak. Automated bots cycle through common passwords at scale. SSH keys eliminate this vector entirely.
Generate a key pair on your local machine if you don't already have one:
ssh-keygen -t ed25519 -C "[email protected]"
Copy the public key to your server:
ssh-copy-id username@your-server-ip
Then disable password authentication in /etc/ssh/sshd_config:
PasswordAuthentication no
ChallengeResponseAuthentication no
Restart sshd. Test the key-based login in a new terminal before closing your existing session.
3. Change the default SSH port
Port 22 gets hammered by automated scanners. Moving SSH to a non-standard port (say, 2849 or 47221) cuts noise in your logs and reduces attack surface. It's security through obscurity, yes, but it works against bulk scanners that only probe common ports.
Edit /etc/ssh/sshd_config:
Port 2849
Update your firewall rules to allow the new port before restarting SSH. Otherwise you'll lock yourself out.
sudo ufw allow 2849/tcp
sudo systemctl restart sshd
Reconnect using ssh -p 2849 username@your-server-ip to verify.
4. Install and configure a firewall
An unconfigured firewall is as good as no firewall. On Debian and Ubuntu systems, UFW (Uncomplicated Firewall) wraps iptables with a simpler interface.
Install UFW if it's not already present:
sudo apt install ufw
Set default policies—deny incoming, allow outgoing:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Allow SSH (use your custom port if you changed it):
sudo ufw allow 2849/tcp
Then allow any services you're running—HTTP, HTTPS, custom application ports:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
Enable the firewall:
sudo ufw enable
Check the rules:
sudo ufw status verbose
For CentOS, RHEL, or AlmaLinux, use firewalld instead.
5. Keep the system updated
Unpatched vulnerabilities are free entry points. Automate updates where possible, at minimum for security patches.
On Debian/Ubuntu, enable unattended upgrades:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
Edit /etc/apt/apt.conf.d/50unattended-upgrades to configure which package origins to auto-update. The defaults are sensible—security updates only.
On RHEL-based systems, use dnf-automatic:
sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic.timer
Even with automation, manually review and apply updates weekly. Kernel updates require reboots, and some package upgrades need service restarts.
6. Disable unused services
Every running service is a potential attack vector. If you're not using it, turn it off.
List active services:
sudo systemctl list-units --type=service --state=running
Disable anything unnecessary. Common candidates: cups (printing), avahi-daemon (Zeroconf), bluetooth, legacy mail transfer agents you replaced.
sudo systemctl disable --now cups
sudo systemctl disable --now avahi-daemon
Be careful with services you don't recognize—some are dependencies for tools you do use. When in doubt, research first.
7. Configure fail2ban to block brute-force attempts
Fail2ban watches log files for repeated authentication failures and automatically bans offending IPs using firewall rules. It's particularly effective against SSH brute-force.
Install fail2ban:
sudo apt install fail2ban
Copy the default config to a local override file:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edit /etc/fail2ban/jail.local and enable the SSH jail:
[sshd]
enabled = true
port = 2849
maxretry = 3
bantime = 3600
Restart fail2ban:
sudo systemctl restart fail2ban
Check banned IPs:
sudo fail2ban-client status sshd
In support tickets I handled, fail2ban typically blocked dozens of IPs per day on internet-facing servers.
8. Set strong password policies
Even with SSH keys, local user passwords matter. Weak passwords enable privilege escalation if an attacker gains initial access through another service.
Install the PAM password quality module:
sudo apt install libpam-pwquality
Edit /etc/security/pwquality.conf:
minlen = 14
minclass = 3
maxrepeat = 2
This enforces a minimum length, requires mixed character classes, and limits repeated characters.
Force password changes on next login for any accounts using weak passwords:
sudo passwd --expire username
9. Limit user privileges with sudo
Don't give users unrestricted sudo access unless they need it. Configure the sudoers file to grant only the specific commands each user requires.
Edit the sudoers file safely:
sudo visudo
Grant a user permission to restart a specific service without full root:
username ALL=(ALL) NOPASSWD: /bin/systemctl restart nginx
For administrators who need broader access, require password confirmation for sudo:
username ALL=(ALL) ALL
Never edit /etc/sudoers directly with a text editor—visudo checks syntax before saving and prevents you from locking everyone out.
10. Enable and configure automatic security updates
This overlaps with step five but deserves emphasis. Automated security patching is not optional for production systems. Manual patching fails when you're on vacation or focused on an outage.
Confirm unattended-upgrades is active:
sudo systemctl status unattended-upgrades
Configure email notifications in /etc/apt/apt.conf.d/50unattended-upgrades so you know what's being patched:
Unattended-Upgrade::Mail "[email protected]";
Unattended-Upgrade::MailReport "on-change";
Install a local mail transfer agent if you don't have one:
sudo apt install postfix
Set it to relay through your primary mail server or a service like SendGrid.
11. Harden shared memory and kernel parameters
Shared memory can be exploited to execute malicious code. Mount /run/shm with noexec:
Edit /etc/fstab and add:
tmpfs /run/shm tmpfs defaults,noexec,nodev,nosuid 0 0
Remount:
sudo mount -o remount /run/shm
Add kernel hardening parameters to /etc/sysctl.conf:
# IP Spoofing protection
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# 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
# Disable source packet routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
# Log Martians
net.ipv4.conf.all.log_martians = 1
Apply the changes:
sudo sysctl -p
These settings protect against IP spoofing, man-in-the-middle attacks, and other network-layer exploits.
12. Enable audit logging
The auditd daemon logs system calls and events. It's essential for forensics when something goes wrong and for compliance in regulated environments.
Install auditd:
sudo apt install auditd
Enable and start the service:
sudo systemctl enable auditd
sudo systemctl start auditd
Add a rule to monitor SSH configuration changes:
sudo auditctl -w /etc/ssh/sshd_config -p wa -k sshd_config
Make rules persistent by adding them to /etc/audit/rules.d/audit.rules.
Search logs for events tagged with your key:
sudo ausearch -k sshd_config
Audit logs grow fast. Rotate them regularly using logrotate.
What did we miss?
These twelve steps form the baseline. They won't stop a determined attacker with time and resources, but they eliminate low-hanging fruit and create multiple layers of defense.
After you've worked through the list, the next level is intrusion detection (AIDE, Tripwire), mandatory access control (SELinux, AppArmor), and centralized logging (syslog-ng to a remote collector). For smaller operations, the checklist above is enough to sleep at night.
FAQ
How long does it take to harden a fresh server?
About an hour if you're following the checklist for the first time. Experienced admins can knock it out in twenty minutes. Automation with Ansible or a shell script brings it down to five.
Should I use SELinux or AppArmor?
If you're on RHEL, CentOS, or Fedora, SELinux is already there—learn to use it in enforcing mode. On Debian and Ubuntu, AppArmor is enabled by default. Both add mandatory access control on top of standard permissions. Don't disable either unless you have a specific reason.
What if I lock myself out?
Most VPS providers offer an out-of-band console through their control panel. Use it to revert SSH configuration changes. This is why you test firewall rules and SSH settings in a separate session before logging out.
Is changing the SSH port really worth it?
It won't stop a targeted attacker, but it cuts the noise from automated bots by more than ninety percent. Your auth logs stay readable and fail2ban has less work to do.
Do I need all twelve steps for a staging server?
Yes. Staging servers often run on the same network as production and sometimes hold real data. Treat them the same.
Run the checklist today
You don't need to harden everything at once. Start with SSH (steps one through three) and the firewall (step four). Those four alone stop the majority of automated attacks. Then work through the rest over the next week.
Every step you skip is a door left unlocked. In production environments I've audited, the compromised servers almost always failed two or three items from this list. Usually SSH was wide open or the firewall was disabled. Sometimes both.
Set a calendar reminder to review these settings quarterly. Software changes, team members come and go, and configurations drift. A hardened server today can become soft in six months if nobody's checking.
![Linux Server Hardening Checklist: 12 Steps [2026]](/images/blog/linux-server-hardening-checklist-12-steps-2026.jpg)