VPS Security Hardening Checklist: 10 Steps to Protect Your Server in 2025
A freshly deployed VPS is not a secure VPS. The moment your server gets a public IP address, automated bots begin scanning it — probing for open ports, attempting SSH brute-force logins, and looking for known vulnerabilities. This isn’t paranoia; it’s the documented reality of internet-facing infrastructure.
The good news: hardening a VPS against the vast majority of attacks takes less than an hour and requires no advanced security expertise. This checklist covers the 10 most impactful steps you can take to protect a Linux VPS in 2025 — in order of importance, with copy-paste commands for Ubuntu and Debian.
Before You Start
This guide assumes you have:
- A KVM VPS running Ubuntu 22.04 or 24.04 LTS
- Root or sudo access via SSH
- A non-root sudo user created (we’ll create one in Step 2 if you haven’t)
Work through these steps in order — some steps depend on previous ones. Most importantly, do not lock yourself out: keep your SSH session open while testing changes, and always have VNC console access available as a fallback (available in VPS.DO’s SolusVM control panel).
💡 VPS.DO Tip: All VPS.DO plans include VNC console access via SolusVM — your emergency backdoor if SSH becomes unreachable during hardening. View VPS Plans →
Step 1: Update Everything Immediately
The single most impactful security action you can take is ensuring your system has all current patches. Many high-profile server compromises exploit known vulnerabilities that were patched months or years earlier — on systems that simply weren’t updated.
sudo apt update && sudo apt upgrade -y
sudo apt autoremove -y
sudo apt autoclean
Check your Ubuntu version and kernel:
lsb_release -a
uname -r
If a kernel update was installed, reboot to load the new kernel:
sudo reboot
Step 2: Create a Non-Root Sudo User
Working as root is dangerous — a typo or compromised session has unlimited destructive potential. Create a dedicated admin user and disable direct root login:
# Create a new user (replace 'adminuser' with your chosen username)
sudo adduser adminuser
# Grant sudo privileges
sudo usermod -aG sudo adminuser
# Switch to the new user to test
su - adminuser
sudo whoami # Should output: root
From this point forward, use adminuser for all SSH connections. Root login will be disabled in Step 4.
Step 3: Configure SSH Key Authentication
Password-based SSH authentication is vulnerable to brute-force attacks. SSH key pairs are cryptographically secure and effectively immune to brute-force. This is the single most important SSH hardening step.
Generate a key pair on your local machine (not the VPS)
# On your local machine (Mac/Linux terminal or Windows PowerShell)
ssh-keygen -t ed25519 -C "your-email@example.com" -f ~/.ssh/vps_key
Ed25519 is the modern, recommended algorithm — smaller and more secure than RSA.
Copy the public key to your VPS
# From your local machine
ssh-copy-id -i ~/.ssh/vps_key.pub adminuser@YOUR_VPS_IP
# Or manually:
cat ~/.ssh/vps_key.pub | ssh adminuser@YOUR_VPS_IP "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Test key-based login before disabling passwords
ssh -i ~/.ssh/vps_key adminuser@YOUR_VPS_IP
Confirm you can log in successfully before proceeding. ✅
Step 4: Harden SSH Configuration
Now lock down the SSH daemon to eliminate the most common attack vectors:
sudo nano /etc/ssh/sshd_config
Make the following changes (find and modify existing lines, or add them if absent):
# Change SSH port (reduces automated scan noise)
Port 2222
# Disable root login entirely
PermitRootLogin no
# Disable password authentication (keys only)
PasswordAuthentication no
ChallengeResponseAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Limit login attempts
MaxAuthTries 3
# Disconnect idle sessions after 10 minutes
ClientAliveInterval 600
ClientAliveCountMax 0
# Only allow specific users to SSH (replace with your username)
AllowUsers adminuser
# Disable X11 forwarding (not needed on a server)
X11Forwarding no
# Use only modern, secure algorithms
Protocol 2
Restart SSH — but keep your current session open while opening a new terminal to test the new configuration:
sudo systemctl restart sshd
In a new terminal, test the connection with the new port:
ssh -i ~/.ssh/vps_key -p 2222 adminuser@YOUR_VPS_IP
Only close your original session once the new connection works. ✅
Step 5: Configure the Firewall (UFW)
UFW (Uncomplicated Firewall) provides an easy interface to manage iptables rules. The principle: deny everything by default, allow only what you explicitly need.
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH on your custom port
sudo ufw allow 2222/tcp
# Allow web traffic (if running a web server)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Enable the firewall
sudo ufw enable
# Verify rules
sudo ufw status verbose
Only open ports you actively use. Every open port is an additional attack surface.
Common port rules reference
# MySQL (only if remote connections are needed — prefer localhost-only)
sudo ufw allow 3306/tcp
# WireGuard VPN
sudo ufw allow 51820/udp
# Custom application port
sudo ufw allow 8080/tcp
# Remove a rule
sudo ufw delete allow 8080/tcp
Step 6: Install and Configure Fail2ban
Even with SSH keys required, bots will still hammer your SSH port with connection attempts. Fail2ban monitors log files and automatically bans IP addresses that show malicious behavior — too many failed login attempts, port scans, and similar patterns.
sudo apt install fail2ban -y
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Create a local configuration file (override defaults without editing the main config):
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# Ban IPs for 1 hour after 5 failures within 10 minutes
bantime = 3600
findtime = 600
maxretry = 5
backend = systemd
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400 # 24 hours for SSH failures
[nginx-http-auth]
enabled = true
[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
sudo systemctl restart fail2ban
Monitor bans in real time:
sudo fail2ban-client status sshd
Step 7: Enable Automatic Security Updates
Manually updating a server is easy to forget. The unattended-upgrades package automatically installs security patches — arguably the most low-effort, high-impact security measure available.
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades
Select “Yes” when prompted. To verify it’s configured correctly:
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
Ensure this line is uncommented:
"${distro_id}:${distro_codename}-security";
Enable automatic updates and upgrades:
sudo nano /etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
Step 8: Disable Unused Services and Daemons
Every running service is a potential attack vector. Remove or disable anything you don’t need:
# List all running services
sudo systemctl list-units --type=service --state=running
# Check which services are listening on network ports
sudo ss -tlnp
# Disable a service (example: cups printing service — useless on a server)
sudo systemctl stop cups
sudo systemctl disable cups
sudo systemctl mask cups
Common services to consider disabling on a typical web server:
cups— Printing service (not needed)avahi-daemon— Network discovery (not needed on a server)bluetooth— Bluetooth (not needed)snapd— Snap package manager (if you don’t use snaps)
# Remove snapd entirely if not using it (saves RAM and reduces attack surface)
sudo systemctl stop snapd
sudo apt purge snapd -y
Step 9: Secure Shared Memory
The /run/shm shared memory filesystem can be used by attackers to execute malicious code. Mount it with restricted options to prevent this:
sudo nano /etc/fstab
Add this line at the bottom:
tmpfs /run/shm tmpfs defaults,noexec,nosuid,nodev 0 0
Apply without rebooting:
sudo mount -o remount /run/shm
Also restrict /tmp to prevent execution of uploaded scripts:
sudo nano /etc/fstab
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
Step 10: Install a Rootkit and Malware Scanner
Automated scanning catches infections you’d otherwise miss. Install two complementary tools:
rkhunter (Rootkit Hunter)
sudo apt install rkhunter -y
# Initialize the baseline database
sudo rkhunter --update
sudo rkhunter --propupd
# Run a full scan
sudo rkhunter --check --sk
chkrootkit
sudo apt install chkrootkit -y
sudo chkrootkit
Schedule weekly scans with Cron
sudo crontab -e
# Run rkhunter every Sunday at 3 AM, email results
0 3 * * 0 /usr/bin/rkhunter --check --sk --report-warnings-only | mail -s "rkhunter report $(hostname)" you@youremail.com
Bonus: Additional Hardening for Production Servers
If you’re running a production server, consider these additional measures beyond the core 10 steps:
Enable Two-Factor Authentication for SSH
sudo apt install libpam-google-authenticator -y
google-authenticator # Follow prompts, scan QR code with authenticator app
Then configure PAM to require 2FA:
sudo nano /etc/pam.d/sshd
Add at the top:
auth required pam_google_authenticator.so
And in /etc/ssh/sshd_config:
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
Install a Web Application Firewall (ModSecurity + Nginx)
sudo apt install libnginx-mod-security2 -y
Set up log monitoring with Logwatch
sudo apt install logwatch -y
Logwatch sends daily email summaries of your server logs — SSH logins, failed attempts, service restarts, and more.
Enable auditd for system call auditing
sudo apt install auditd -y
sudo systemctl enable auditd
sudo systemctl start auditd
Security Hardening Checklist Summary
| # | Step | Priority | Time |
|---|---|---|---|
| 1 | Update all packages | 🔴 Critical | 2 min |
| 2 | Create non-root sudo user | 🔴 Critical | 2 min |
| 3 | Configure SSH key authentication | 🔴 Critical | 5 min |
| 4 | Harden SSH config (disable password auth, change port) | 🔴 Critical | 5 min |
| 5 | Configure UFW firewall | 🔴 Critical | 5 min |
| 6 | Install and configure Fail2ban | 🟠 High | 5 min |
| 7 | Enable automatic security updates | 🟠 High | 3 min |
| 8 | Disable unused services | 🟡 Medium | 10 min |
| 9 | Secure shared memory | 🟡 Medium | 3 min |
| 10 | Install rootkit scanner | 🟡 Medium | 5 min |
Total time: approximately 45 minutes for a fully hardened VPS.
Final Thoughts
VPS security hardening is not a one-time task — it’s an ongoing practice. But the steps in this guide establish a strong baseline that addresses the most common attack vectors and dramatically reduces your server’s exposure. SSH keys, a properly configured firewall, Fail2ban, and automatic updates together eliminate the majority of real-world VPS compromises.
Run through this checklist on every new VPS you deploy. Better yet, turn it into a setup script so hardening becomes automatic on every new server. Your future self will thank you the first time a brute-force campaign targets your IP and finds nothing to attack.
All VPS.DO KVM VPS plans come with full root access and VNC console fallback — everything you need to implement this checklist safely. View USA VPS Plans →
Need help with a specific security configuration? VPS.DO’s support team is available 24/7 →
Related articles you might find useful: