How to Harden Your Linux VPS in 10 Minutes

Flat illustration of a glowing hexagonal padlock in front of a brick firewall and terminal window with SSH commands, representing Linux VPS hardening

The moment you spin up a fresh Linux VPS, the clock starts ticking. Within hours — sometimes minutes — your IP shows up in scanner logs and bots begin trying default credentials, common SSH usernames, and known web exploits. I've watched a brand-new server log over four thousand brute-force SSH attempts in its first 24 hours of life.

Most of those attacks are stoppable in 10 minutes of work. Here's the no-fluff checklist I run on every new VPS — the same one I used when I built byte-guard.net itself.

What You'll Need

  • A fresh VPS running Ubuntu 22.04+ or Debian 11+ (most steps work on any modern distro)
  • Root SSH access — ideally a just-provisioned server, before you've done anything else
  • 10 minutes
  • An SSH key on your local machine (we'll generate one if you don't have it)
Note: these commands assume apt-based distros. If you're on Rocky, Alma, or RHEL, swap apt for dnf and ufw for firewalld — the principles are identical.

Step 1 — Update Everything

Bots love unpatched systems. The first thing to do on any new server is apply outstanding updates:

apt update && apt upgrade -y

This pulls down the package index and installs every available update. On a fresh VPS this typically takes 1-2 minutes.

Step 2 — Create a Non-Root User

You should never SSH in as root for daily work. If your root account gets compromised, you've handed an attacker complete control. A regular user with sudo access gives you the same power but keeps an audit trail and adds a small barrier between mistakes and disaster.

adduser amine
usermod -aG sudo amine

Replace amine with your username. adduser will prompt you for a password — make it strong (a passphrase from pwgen -s 32 1 is excellent), but you'll mostly be using SSH keys after the next step.

Step 3 — Set Up SSH Key Authentication

Passwords get brute-forced. Ed25519 SSH keys don't, in any practical sense. If you don't have one yet, generate it on your local machine, not the server:

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

Why ed25519 over rsa? It's faster, smaller, and more modern. The default rsa 3072-bit key is also fine, but ed25519 is the current best practice.

Then copy it to the server, replacing the placeholder with your user and IP:

ssh-copy-id amine@your-server-ip

Now test it from a new terminal:

ssh amine@your-server-ip

You should log in without being asked for a password. If that works, you're ready to lock down SSH itself.

Step 4 — Lock Down SSH

This is the single biggest security win. Open the SSH server config:

sudo vim /etc/ssh/sshd_config

Find and change these lines (uncomment them if needed):

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM no

What each does:

  • PermitRootLogin no — root cannot SSH in at all
  • PasswordAuthentication no — only SSH keys work, no passwords
  • PubkeyAuthentication yes — explicitly enable SSH keys (usually default but be explicit)
  • ChallengeResponseAuthentication no and UsePAM no — close fallback authentication paths
⚠️ Don't close your current session yet. Test that you can log in via key from a new terminal first. If you've made a config mistake, you'll need that working session to fix it.

Save and reload SSH:

sudo systemctl reload sshd

Open a brand new terminal and SSH in as your user. If it works, your server is now key-only. Now you can safely close the old root session.

Step 5 — Set Up UFW (the Firewall)

ufw is Ubuntu's user-friendly firewall. It ships with most modern distros and just needs to be enabled with a sensible default policy:

sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw enable

Verify the rules:

sudo ufw status verbose

You should see only port 22 (SSH) open. If you're running a web server, also allow:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Don't allow ports you're not actually using. Every open port is a potential attack surface.

Step 6 — Install fail2ban

Even with key-only SSH, your logs will fill up with rejected brute-force attempts. fail2ban watches the auth log and bans IPs that repeatedly fail to authenticate:

sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

Out of the box, the default config protects SSH. Check that the SSH jail is active:

sudo fail2ban-client status sshd

You should see something like:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 0
   |- Total banned:     0
   `- Banned IP list:

To tighten the defaults (out of the box: 5 attempts, 10-minute ban), create a local override:

sudo vim /etc/fail2ban/jail.local

Add:

[sshd]
enabled = true
maxretry = 3
findtime = 10m
bantime = 1h

Then reload:

sudo systemctl restart fail2ban

Three failed attempts in 10 minutes now earns a one-hour ban. Aggressive enough to deter bots, lenient enough that you can recover from your own typos.

Step 7 — Enable Unattended Upgrades

Security patches matter most when they actually get installed. Unattended upgrades automatically apply security updates so you don't have to remember to log in and apt upgrade:

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

Choose Yes when prompted. This installs a systemd timer that runs daily and applies security updates only — not feature upgrades, so you won't get surprise breaking changes.

Verify it's running:

sudo systemctl status unattended-upgrades

You should see active (running).

Step 8 — Sanity Check

Run these to verify everything is in place:

# SSH config — both should say "no"
sudo sshd -T | grep -E "permitrootlogin|passwordauthentication"

# Firewall — should show only the ports you opened
sudo ufw status

# fail2ban — should show the sshd jail as active
sudo fail2ban-client status sshd

# Unattended upgrades — should be active
sudo systemctl is-active unattended-upgrades

If everything checks out, your VPS is hardened against the most common automated attacks.

Bonus — Change the SSH Port (Optional)

Moving SSH off port 22 doesn't add real security (it's security through obscurity), but it does massively cut log noise from drive-by scanners. Edit /etc/ssh/sshd_config:

Port 2222

Then update UFW:

sudo ufw delete allow OpenSSH
sudo ufw allow 2222/tcp
sudo systemctl reload sshd

Connect with ssh -p 2222 amine@your-server-ip. Add it to your ~/.ssh/config so you never type the port again:

Host my-vps
    HostName your-server-ip
    User amine
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Now you can just type ssh my-vps.

What This Does NOT Cover

10 minutes gets you the essentials. It does not cover:

  • Application-layer security — if you're running a web app, you still need to harden Nginx, your reverse proxy, your CMS, and so on
  • Intrusion detection — tools like AIDE or Wazuh for filesystem integrity and behavioral monitoring
  • Centralized logging — shipping logs to a separate server so an attacker who lands on the box can't quietly cover their tracks
  • Backups — hardening means nothing if you can't restore after an incident

I'll cover those in future posts. For now, you've blocked the overwhelming majority of automated attacks that hit any new VPS.

What's Next

If you're spinning up a VPS for self-hosting (and you should — it's the best way to actually learn how things work), check out the full byte-guard.net build: How I Built byte-guard.net from Scratch on a Hetzner VPS. It uses every step in this post and adds Docker, a reverse proxy, and monitoring on top.

Need a VPS to practice on? I run everything on Hetzner — solid hardware, great pricing, and a clean control panel. [affiliate link coming soon]


Quick recap — the 10-minute checklist:

  1. apt update && apt upgrade
  2. ✅ Create non-root user with sudo
  3. ✅ SSH key auth set up
  4. ✅ Root login + password auth disabled in sshd_config
  5. ✅ UFW firewall enabled, only the ports you need
  6. ✅ fail2ban watching the SSH jail
  7. ✅ Unattended security updates running

Run this on every new server you build. After a few times you'll be doing it in closer to 5 minutes than 10.