Linux Server Hardening: A Practical Checklist for Production Systems

A fresh Linux server is not a secure server. This guide covers the practical hardening steps — SSH lockdown, firewall rules, kernel tuning, and more — that close off the most common attack vectors on production systems.

Most servers get compromised not through sophisticated zero-day exploits, but through basic misconfigurations, forgotten defaults, and skipped updates. A freshly provisioned Linux server is not a secure server. It's a starting point.

This guide walks through the hardening steps that actually matter — the ones that close off the most common attack vectors on production systems.

Start with User and SSH Access

SSH is the front door to your server. It's also the most commonly probed service on the internet. Bots are scanning for open port 22 right now, trying default credentials and known exploits.

Disable Root Login and Password Authentication

The first two changes you should make on any new server:

# In /etc/ssh/sshd_config PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes

Disabling root login forces attackers to know both a valid username and have the correct SSH key. Combined with disabling password auth entirely, you eliminate brute-force attacks from the equation.

After editing, reload the daemon:

systemctl reload sshd

Change the Default SSH Port

This is security through obscurity — not a real defense on its own, but it eliminates enormous amounts of automated noise from your logs:

# In /etc/ssh/sshd_config Port 2222

Remember to open the new port in your firewall before reloading SSH, or you'll lock yourself out.

Create a Dedicated Admin User

Never use a generic username like admin or ubuntu for day-to-day access. Create a named user with sudo privileges:

useradd -m -s /bin/bash yourname usermod -aG sudo yourname mkdir -p /home/yourname/.ssh cp ~/.ssh/authorized_keys /home/yourname/.ssh/ chown -R yourname:yourname /home/yourname/.ssh chmod 700 /home/yourname/.ssh chmod 600 /home/yourname/.ssh/authorized_keys

Firewall Configuration with UFW

A firewall is non-negotiable. On Ubuntu and Debian systems, UFW gives you a straightforward interface to iptables that's hard to get wrong.

The default posture should be: deny everything, then explicitly allow what you need.

ufw default deny incoming ufw default allow outgoing ufw allow 2222/tcp # Your custom SSH port ufw allow 80/tcp ufw allow 443/tcp ufw enable

Check the status at any time:

ufw status verbose

If you're running a database like MySQL or PostgreSQL, do not open those ports publicly. They should only be accessible via localhost or a private network interface. A PostgreSQL instance exposed on port 5432 to the internet is an invitation for trouble.

Keep the System Updated — Automatically

Unpatched packages are responsible for a disproportionate share of real-world compromises. The fix is simple: keep things updated.

Enable Unattended Upgrades

On Debian/Ubuntu:

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

The key configuration lives in /etc/apt/apt.conf.d/50unattended-upgrades. At minimum, enable automatic security updates:

Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; }; Unattended-Upgrade::Automatic-Reboot "false"; Unattended-Upgrade::Mail "you@yourdomain.com";

Setting Automatic-Reboot to false on production systems lets you control when kernel reboots happen — important for uptime. You'll still get an email when a reboot is needed.

On managed hosting plans, kernel updates and OS-level patching are typically handled for you. But if you're managing your own VPS, unattended upgrades are not optional.

Harden the Kernel with sysctl

The Linux kernel exposes a lot of network and memory behavior through sysctl. Several defaults are fine for development but should be tightened on production servers.

Add the following to /etc/sysctl.d/99-hardening.conf:

# Disable IP source routing net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 # Ignore ICMP redirects net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 # Enable SYN flood protection net.ipv4.tcp_syncookies = 1 # Disable IP forwarding (unless this is a router) net.ipv4.ip_forward = 0 # Protect against time-wait assassination net.ipv4.tcp_rfc1337 = 1 # Restrict dmesg to root kernel.dmesg_restrict = 1 # Prevent core dumps from setuid programs fs.suid_dumpable = 0

Apply the changes immediately:

sysctl -p /etc/sysctl.d/99-hardening.conf

Audit Running Services

Every service running on your server is a potential attack surface. Audit what's actually running:

ss -tulnp

This shows all listening sockets with the associated process. Go through the output line by line. If you don't recognize a service, investigate it. If you don't need it, disable it:

systemctl stop service-name systemctl disable service-name

Common offenders on default installs include: avahi-daemon, cups, and various Bluetooth services — none of which belong on a headless production server.

Set Up Fail2ban for Intrusion Detection

Fail2ban monitors log files and temporarily bans IPs that show malicious behavior — repeated failed SSH logins, for example.

apt install fail2ban

Create a local override file (so your config survives package updates):

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

Then configure the SSH jail in /etc/fail2ban/jail.local:

[sshd] enabled = true port = 2222 filter = sshd logpath = /var/log/auth.log maxretry = 4 bantime = 3600 findtime = 600

This bans any IP that fails SSH authentication 4 times within 10 minutes, for one hour. Adjust these thresholds to match your risk tolerance.

Backups: Your Last Line of Defense

Hardening reduces your attack surface, but no hardening is perfect. If something does go wrong — a compromised credential, a zero-day, a misconfiguration — backups are what let you recover without catastrophic data loss.

The key metrics for any backup strategy are RPO (Recovery Point Objective) and RTO (Recovery Time Objective). RPO defines how much data you can afford to lose; RTO defines how fast you need to be back online.

A single daily backup gives you an RPO of up to 24 hours. For most production websites that's acceptable, but for high-transaction applications — e-commerce, SaaS products, anything writing data continuously — it often isn't. Running backups multiple times a day (we support up to four per day, with configurable full or partial runs) dramatically shrinks that window without proportionally increasing storage costs, since intra-day runs can be partial backups covering only databases and changed files.

Whatever backup strategy you choose, verify it regularly. A backup you've never tested is a backup you can't trust.

A Hardened Server Is a Maintained Server

Hardening isn't a one-time task you check off and forget. It's an ongoing practice. Services change, new CVEs drop, and configurations drift over time — especially on servers touched by multiple people.

Schedule a quarterly audit: review running services, check SSH config, verify firewall rules are still accurate, and confirm your backups are working. Tools like lynis can automate much of this:

apt install lynis lynis audit system

Lynis scores your system and gives specific, prioritized recommendations. It's not a replacement for understanding what you're doing, but it's an excellent sanity check.

The servers that get compromised are almost always the ones nobody was actively watching. Stay deliberate, stay current, and treat security as infrastructure — not an afterthought.