The 15-Minute VPS Security Masterclass: Lock Down Your Linux Server
A Practical Beginner-to-Advanced Guide to Hardening Your Linux VPS Against Hackers, Bots, Malware, and Zero-Day Threats
Senior Developer

You spin up a new VPS. The provider sends you root credentials. You SSH in and get straight to deploying your app because that is the exciting part.
Meanwhile, bots have already tried to log into your server 400 times while you were setting up your repo.
This is not paranoia. Run grep "Failed password" /var/log/auth.log | wc -l on any unprotected server that has been live for more than 24 hours. The number will be uncomfortable.
Securing a VPS is not complicated. It takes about 15 minutes, you do it once, and then you stop worrying about a category of problems that has ended real companies. Here is the complete checklist.
Step 1 — Create a Non-Root User (Do This First)
Never run anything as root day-to-day. Create a user immediately after first login.
# As root
adduser yourname
usermod -aG sudo yourname
# Switch to the new user and test sudo
su - yourname
sudo apt updateStep 2 — Set Up SSH Key Authentication
Password-based SSH logins will be disabled entirely. First, set up key-based auth so you do not lock yourself out.
On your local machine:
# Generate a key if you don't already have one
ssh-keygen -t ed25519 -C "your-email@example.com"
# Copy public key to your server
ssh-copy-id yourname@your-server-ipOr manually:
# On the server, as yourname
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# Paste your public key into this file
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keysTest that key-based login works before disabling passwords:
# From a new terminal on your local machine — confirm this works
ssh yourname@your-server-ipStep 3 — Harden SSH Config
sudo nano /etc/ssh/sshd_configChange or add these lines:
# Disable root login
PermitRootLogin no
# Disable password authentication (keys only)
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM no
# Change the default port (reduces noise from bots)
Port 2222
# Only allow specific users
AllowUsers yourname
# Reduce login grace time
LoginGraceTime 20
# Disable X11 forwarding unless you need it
X11Forwarding noRestart SSH — do not close your current session first:
sudo systemctl restart sshdOpen a new terminal and test logging in with the new port:
ssh -p 2222 yourname@your-server-ipOnce confirmed, close the old session.
Step 4 — Configure UFW (Uncomplicated Firewall)
sudo apt install ufw -y
# Deny everything by default
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH on your new port
sudo ufw allow 2222/tcp
# Allow web traffic
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable the firewall
sudo ufw enable
# Verify
sudo ufw status verboseIf you need other ports (e.g. Postgres from a specific IP only):
# Only allow Postgres from a specific trusted IP
sudo ufw allow from 203.0.113.0 to any port 5432Never expose Postgres, Redis, or any database port to 0.0.0.0. Ever.
Step 5 — Install Fail2Ban
Fail2Ban watches your auth logs and automatically bans IPs that fail login attempts repeatedly.
sudo apt install fail2ban -y
# Create a local override config (never edit jail.conf directly)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.localFind and update the [sshd] section:
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600Start and enable:
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Check it's working
sudo fail2ban-client status sshdStep 6 — Automatic Security Updates
You want security patches applied without having to remember to do it manually.
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgradesEdit the config to be explicit:
sudo nano /etc/apt/apt.conf.d/50unattended-upgradesUncomment and verify these lines are present:
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}-security";
};
Unattended-Upgrade::Automatic-Reboot "false";
Unattended-Upgrade::Mail "you@yourdomain.com";Set it to run daily:
sudo nano /etc/apt/apt.conf.d/20auto-upgradesAPT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";Step 7 — Set Up a Basic Audit Log
Install auditd to track file and system changes you care about:
sudo apt install auditd -y
sudo systemctl enable auditd
sudo systemctl start auditd
# Watch for changes to sensitive files
sudo auditctl -w /etc/passwd -p wa -k passwd_changes
sudo auditctl -w /etc/ssh/sshd_config -p wa -k ssh_config_changes
sudo auditctl -w /var/log/auth.log -p ra -k auth_log_reads
# Make rules persistent
sudo nano /etc/audit/rules.d/hardening.rulesAdd:
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /etc/sudoers -p wa -k sudoers_changesStep 8 — Disable Unused Services
Check what is listening on your server:
sudo ss -tulpnFor every service you do not recognize or need, stop and disable it:
sudo systemctl stop <service-name>
sudo systemctl disable <service-name>Common ones you probably do not need on a fresh VPS: cups (printing), avahi-daemon (network discovery), bluetooth.
Step 9 — Set Up Logwatch or Similar Alerting
Install logwatch to get a daily email digest of anything suspicious:
sudo apt install logwatch -y
# Test it
sudo logwatch --output stdout --format text --range todayConfigure for daily emails:
sudo nano /etc/cron.daily/00logwatch#!/bin/bash
/usr/sbin/logwatch --output mail --mailto you@yourdomain.com --detail highStep 10 — The Quick Sanity Check
After everything is set up, run through this:
# 1. Confirm root login is disabled
ssh root@your-server-ip -p 2222 # Should be refused
# 2. Confirm UFW is active
sudo ufw status
# 3. Confirm Fail2Ban is running
sudo fail2ban-client status
# 4. Check what ports are open externally
nmap -p- your-server-ip # Install nmap locally
# 5. Check for failed login attempts
sudo grep "Failed password" /var/log/auth.log | tail -20
# 6. Check for successful logins (verify only yours)
sudo last | head -20What This Does Not Cover
This guide handles the basics that catch the overwhelming majority of attacks targeting VPS instances. What it does not cover — and what matters at the next level:
SSL certificate management (use Certbot with Let's Encrypt; automate renewal)
Container security if you are running Docker (AppArmor profiles, non-root containers, no
--privileged)Application-level secrets management (use environment variables or a secrets manager, never hardcode credentials)
Database hardening (separate Postgres post incoming)
A hardened VPS is not invulnerable. It is significantly less interesting to attackers than the unprotected one next door. In practice, that is usually enough.
Comments (0)
Login to post a comment.