A freshly provisioned Linux VPS arrives in a predictably insecure state. The root account has SSH access, no firewall rules are active, and every service runs as installed — with default configurations that prioritize compatibility over security.

Most compromises on VPS instances happen within hours of provisioning, not weeks. Automated scanners probe the entire IPv4 space continuously, looking for SSH on port 22 with root login enabled. What follows is a sequential hardening process you can complete in under an hour on any Debian or Ubuntu-based VPS.

Step 1: Create a Non-Root User and Lock Down Root

Log in as root for the last time. Your first task is to create a regular user account that you will use for all future access.

adduser yourusername
usermod -aG sudo yourusername

Once you have confirmed that this user can run sudo commands, disable direct root SSH login. Open /etc/ssh/sshd_config and set PermitRootLogin no. This single change eliminates every attack that targets the root account over SSH directly, which represents a significant portion of automated brute-force attempts against VPS instances.

Why This Matters

The root account is the universal target. Its name is known, its UID is 0, and a successful login gives an attacker complete control with no further privilege escalation required. A named user account adds an unknown variable — the attacker must guess both the username and the password.

Step 2: Set Up SSH Key Authentication

Password-based SSH authentication is vulnerable to brute force. SSH keys are not. Generate a 4096-bit RSA key pair on your local machine, or use an Ed25519 key, which produces shorter keys with equivalent security under current threat models.

ssh-keygen -t ed25519 -C "your_email@example.com"

Copy the public key to your server:

ssh-copy-id yourusername@your_server_ip

With the key in place, open /etc/ssh/sshd_config and set PasswordAuthentication no. Restart the SSH daemon with systemctl restart sshd. Do not close your existing session until you have verified you can connect using the key in a new terminal window. Locking yourself out of a VPS is a common and avoidable mistake.

Additional SSH Hardening Options

While you have sshd_config open, apply these additional settings:

  • Port 2222 — Move SSH off port 22. Obscurity is not security, but it does eliminate most automated scans.
  • MaxAuthTries 3 — Limit the number of authentication attempts per connection.
  • ClientAliveInterval 300 and ClientAliveCountMax 2 — Terminate idle sessions after ten minutes.
  • AllowUsers yourusername — Whitelist the only account permitted to connect.

Step 3: Configure a Firewall

Ubuntu ships with ufw (Uncomplicated Firewall), a front end for iptables that covers most VPS use cases without requiring you to write raw iptables rules. Debian includes it as well, though you may need to install it manually.

apt install ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp   # or whichever port you set SSH to
ufw enable

Add rules only for services you are actually running. If your VPS will serve HTTP and HTTPS traffic, add ufw allow 80/tcp and ufw allow 443/tcp. Every open port is an attack surface. A web server that also has an exposed Redis port, for example, is a common misconfiguration that has led to full database exposure in real-world incidents.

Verify Your Rules

Run ufw status verbose to confirm the active ruleset. The output should show a default deny policy for incoming connections and explicit allow rules only for the ports you need. If you moved SSH to a non-standard port, verify it is listed correctly before closing your session.

Step 4: Install Fail2ban

Fail2ban monitors log files and temporarily bans IP addresses that show signs of brute-force behavior. It works by writing iptables rules that block offending IPs for a configurable duration.

apt install fail2ban

Fail2ban’s default configuration watches /var/log/auth.log and bans IPs after five failed SSH attempts within ten minutes. The ban lasts ten minutes by default. You can increase the sensitivity by creating a local override in /etc/fail2ban/jail.local:

[sshd]
enabled = true
port = 2222
maxretry = 3
bantime = 3600
findtime = 600

This configuration bans an IP for one hour after three failed attempts within ten minutes. The port directive must match whatever port you configured in sshd_config. Restart Fail2ban after any configuration change: systemctl restart fail2ban.

Step 5: Keep the System Updated

Unpatched software is the most common vector for privilege escalation once an attacker has any foothold on a system. Run a full update immediately after provisioning:

apt update && apt upgrade -y

For ongoing maintenance, enable unattended security updates. The unattended-upgrades package handles this automatically on Debian-based systems:

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

This configures the system to automatically apply security patches from the distribution’s security repository without requiring manual intervention. It does not apply major version upgrades or install new packages — only patches to already-installed software.

Step 6: Disable Unused Services

Every running service that you did not deliberately install is an unnecessary risk. Check what is running:

systemctl list-units --type=service --state=running

Common services to disable on a minimal VPS include avahi-daemon (mDNS discovery, rarely needed on a server), cups (printer management), and bluetooth. Disabling a service prevents it from starting automatically and stops it immediately:

systemctl disable --now avahi-daemon

Review the list carefully. If you do not know what a service does, look it up before disabling it — some services support core system functionality.

Step 7: Harden the Kernel with sysctl

The Linux kernel exposes a large number of tuneable parameters via /proc/sys/, manageable through sysctl. Several of these have direct security implications. Add the following to /etc/sysctl.d/99-hardening.conf:

# Disable IP forwarding (unless this is a router)
net.ipv4.ip_forward = 0

# Ignore ICMP broadcast requests
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Disable source routing
net.ipv4.conf.all.accept_source_route = 0

# Enable SYN flood protection
net.ipv4.tcp_syncookies = 1

# Do not accept ICMP redirects
net.ipv4.conf.all.accept_redirects = 0

# Log martian packets
net.ipv4.conf.all.log_martians = 1

Apply the changes immediately with sysctl --system. These settings reduce the VPS’s exposure to network-level attacks including SYN floods and IP spoofing, and prevent the server from being used as a routing relay without your knowledge.

Step 8: Review File Permissions and SUID Binaries

SUID (Set User ID) binaries run with the permissions of their owner rather than the user executing them. Legitimate SUID binaries exist — passwd, sudo, and ping are common examples. Unexpected SUID binaries are a common persistence mechanism used after a compromise.

find / -perm -4000 -type f 2>/dev/null

Document the output on a clean system and compare it periodically. Any new SUID binary that you did not install yourself warrants immediate investigation. Tools like aide (Advanced Intrusion Detection Environment) can automate this monitoring by maintaining a cryptographic hash database of filesystem state.

Closing

This guide covers the baseline. A hardened VPS is not a finished product — it is a starting configuration that requires ongoing attention. Log monitoring, regular audits of open ports with ss -tulnp, and periodic review of user accounts all form part of a functional maintenance routine. The configuration choices made in the first thirty minutes after provisioning determine how much work an attacker must do to gain a foothold; the OpenSSH project’s own documentation notes that disabling password authentication eliminates the majority of automated attack traffic targeting SSH listeners.