# Linux Server Hardening Checklist: What I Do on Every Fresh VPS

> A practical Linux server hardening checklist covering SSH lockdown, firewall rules, automatic updates, and fail2ban — steps I apply to every DigitalOcean and GCP instance.

URL: https://www.matthewswong.com/en/blog/linux-server-hardening/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2025-06-10
- Updated: 2026-10-17
- Category: DevOps

The average internet-facing Linux server receives hundreds of SSH brute-force attempts daily. Within minutes of provisioning a fresh DigitalOcean Droplet, bots are already scanning port 22. Servers with a weak root password are routinely compromised soon after going online. For that reason, I run the same hardening checklist on every new server before deploying any application. This guide documents exactly what I do and why each step matters.

## Step 1: Secure SSH Access

SSH is the front door of every Linux server and the highest-value target for attackers. The CIS Benchmark for Ubuntu recommends disabling root login, disabling password authentication, and using SSH key pairs as the minimum baseline. SSH keys are cryptographically immune to brute-force — a 2048-bit RSA key has more possible combinations than atoms in the observable universe. Password authentication against a determined attacker with a distributed botnet is a losing game.

### SSH Config Hardening

Edit /etc/ssh/sshd_config with these critical settings: PermitRootLogin no, PasswordAuthentication no, MaxAuthTries 3, PubkeyAuthentication yes, X11Forwarding no, AllowTcpForwarding no (unless needed), and change the port from 22 to a non-standard port (e.g., 2222) to reduce automated scan noise. After making changes, always test the new config with sshd -t before reloading — a broken sshd config with no active session will lock you out of your own server.

### Firewall with UFW

UFW (Uncomplicated Firewall) is the right tool for straightforward VPS firewall management. Start with a default-deny-all-incoming policy, then whitelist only what you need: your SSH port, HTTP (80), and HTTPS (443). Never open database ports (5432, 3306, 6379) to the public internet — databases should only be reachable on the loopback interface or a private network. On DigitalOcean, combine UFW with the Cloud Firewall for defense in depth.

> From my experience hardening servers in Jakarta with limited admin bandwidth, set up UFW before changing your SSH port. The sequence matters: add the new SSH port rule (ufw allow 2222/tcp), enable UFW, then change sshd_config port and reload SSH. Doing it out of order has locked me out twice — you end up opening a DigitalOcean console session to fix it, which is embarrassing and time-consuming.

## Step 2: Automatic Security Updates

Unpatched packages are responsible for a significant portion of server compromises. On Ubuntu/Debian, unattended-upgrades handles automatic security updates without requiring manual intervention. Install it, enable automatic reboots during a low-traffic window, and configure email notifications for reboot events. The Qualys Threat Research Unit found that 60% of breaches involved a vulnerability for which a patch was available but not applied.

### System-Level Hardening

Beyond SSH and firewall, apply kernel-level hardening via /etc/sysctl.conf: disable IP forwarding (unless this is a router), enable SYN cookies to prevent SYN flood attacks, set kernel.randomize_va_space=2 for ASLR, and restrict core dumps. Install and configure fail2ban for automated IP banning on repeated auth failures. Remove unused packages and disable unnecessary services with systemctl disable to reduce attack surface.

> **Audit Before Hardening**

> I once applied CIS Level 2 hardening to a production NestJS server without testing it first — it disabled a kernel feature that the Node.js runtime relied on for performance, causing intermittent crashes under load. Always test hardening changes in a staging environment that mirrors production. Run CIS-CAT or Lynis after applying changes to audit what you actually hardened versus what you think you hardened. The difference can be significant.

## Step 3: Intrusion Detection

Fail2ban is the minimum viable intrusion detection for a VPS. It monitors log files and bans IPs exceeding failure thresholds. Beyond fail2ban, consider installing OSSEC or Wazuh for file integrity monitoring — they alert you when critical system files change, which is a strong signal of compromise. For compliance-sensitive environments, auditd provides kernel-level audit logging of system calls, file access, and user actions.

## Step 4: User and Permission Management

Never run application workloads as root. Create a dedicated system user for each service (e.g., www-data for Nginx, nestjs for your Node.js app) with minimal permissions. Use sudo for privilege escalation and configure /etc/sudoers carefully — log all sudo commands with visudo's NOPASSWD disabled. Audit existing users regularly with getent passwd and remove any that should not exist. On fresh VPS instances, disable or delete the default ubuntu or debian user if you have set up your own admin user.

## My Production Hardening Script

I maintain a bash script that runs all these steps idempotently — I can run it on a fresh server or re-run it on an existing one without breaking anything. It sets SSH config, configures UFW, installs and configures fail2ban, enables unattended-upgrades, applies sysctl hardening, and removes unnecessary packages. Keep the script in a private repository and make it the first thing that runs after spinning up any new server. Automating the checklist eliminates human error and ensures consistency across every environment.

## Sources & Further Reading

- CIS Security — CIS Benchmarks for Ubuntu Linux
- SysWard — Server Security Hardening Guide 2025
- LinuxTeck — Linux Server Hardening Checklist 40+ Steps

## Source links

- https://www.cisecurity.org/benchmark/ubuntu_linux
- https://www.linuxteck.com/linux-server-hardening-checklist/

## FAQ

### Why should I disable password authentication and use SSH keys instead?

SSH keys are cryptographically immune to brute-force attacks — a 2048-bit RSA key has more possible combinations than atoms in the observable universe. Password authentication against a determined attacker with a distributed botnet is a losing game. Disabling it, along with root login, is the minimum baseline recommended by the CIS Benchmark for Ubuntu.

### What is the correct order for changing the SSH port and setting up UFW to avoid getting locked out?

You must add the new SSH port rule to UFW first (e.g., ufw allow 2222/tcp), enable UFW, and only then change the port in sshd_config and reload SSH. Doing it out of order — enabling the firewall before allowing the new port — will block your own SSH connection and require a DigitalOcean console session to recover.

### Why are automatic security updates important, and how significant is the risk of skipping them?

Unpatched packages are responsible for a significant portion of server compromises. The Qualys Threat Research Unit found that 60% of breaches involved a vulnerability for which a patch was available but not applied. On Ubuntu/Debian, unattended-upgrades handles this automatically without manual intervention.

### Should I test hardening changes directly on a production server?

No — always test hardening changes in a staging environment that mirrors production first. Applying CIS Level 2 hardening directly to a production NestJS server without testing disabled a kernel feature that the Node.js runtime relied on for performance, causing intermittent crashes under load. Use CIS-CAT or Lynis after applying changes to audit what was actually hardened.

### What should I do beyond SSH hardening and firewall rules to reduce attack surface?

Apply kernel-level hardening via /etc/sysctl.conf (disable IP forwarding, enable SYN cookies, set ASLR with kernel.randomize_va_space=2), install and configure fail2ban for automated IP banning, and remove unused packages and disable unnecessary services. For deeper intrusion detection, consider OSSEC or Wazuh for file integrity monitoring, and auditd for kernel-level audit logging in compliance-sensitive environments.

---

- HTML version: https://www.matthewswong.com/en/blog/linux-server-hardening/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/linux-server-hardening/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
