Skip to main content
Back to blog
April 28, 2026·Updated April 28, 2026|10 min read|Antoine Duno

VPS Security Hardening: The Complete 2026 Checklist

VPS security hardening is the difference between a server that gets compromised within hours of provisioning and one that stays secure for years. This complete checklist covers SSH hardening, fail2ban, UFW, CrowdSec, SSL, and web security headers.

ZeriFlow Team

1,356 words

VPS Security Hardening: The Complete 2026 Checklist

VPS security is not optional — an unprotected server on a public IP address is typically scanned and attacked within minutes of provisioning. The good news is that a basic hardening checklist applied in the first hour of setup provides substantial protection against the vast majority of automated attacks.

This guide covers every essential VPS security layer: SSH hardening, fail2ban intrusion prevention, UFW firewall, CrowdSec collective intelligence, SSL configuration, and web security headers. Each section includes specific commands you can run directly.

After configuring your web server, run a ZeriFlow scan on your domain to verify your HTTPS setup, security headers, and public-facing security posture.


SSH Hardening: Eliminating the Biggest Attack Vector

SSH brute force is the most common attack against VPS instances. A default SSH configuration accepts password authentication on port 22 — both of which make brute force trivial.

Step 1: Create a non-root user

bash
adduser deployuser
usermod -aG sudo deployuser

Step 2: Set up SSH key authentication On your local machine:

bash
ssh-keygen -t ed25519 -C 'your-email@domain.com'
ssh-copy-id -i ~/.ssh/id_ed25519.pub deployuser@YOUR_SERVER_IP

Step 3: Harden SSH configuration Edit /etc/ssh/sshd_config:

bash
# Disable root login
PermitRootLogin no

# Disable password authentication (only after confirming key auth works)
PasswordAuthentication no
PubkeyAuthentication yes

# Change default port (optional but reduces log noise significantly)
Port 2222

# Limit authentication attempts
MaxAuthTries 3

# Disconnect idle sessions after 5 minutes
ClientAliveInterval 300
ClientAliveCountMax 0

# Disable X11 forwarding (not needed for web servers)
X11Forwarding no

# Disable empty passwords
PermitEmptyPasswords no

# Allow only specific users
AllowUsers deployuser

After editing, test your configuration before restarting:

bash
sshd -t  # Test for syntax errors
systemctl restart sshd

Critical: Keep your current SSH session open while testing login with your key from a new terminal window. Only close the original session after confirming the new configuration works.


UFW Firewall: Default Deny, Explicit Allow

UFW (Uncomplicated Firewall) provides a human-readable interface to iptables. The strategy is simple: deny all incoming traffic by default, then explicitly allow only what you need.

bash
# Install UFW
apt-get install ufw

# Set default policies
ufw default deny incoming
ufw default allow outgoing

# Allow SSH (use your custom port if you changed it)
ufw allow 2222/tcp comment 'SSH'

# Allow HTTP and HTTPS
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'

# Enable UFW
ufw enable

# Verify rules
ufw status verbose

If you changed your SSH port, make sure to run ufw allow YOUR_NEW_PORT/tcp BEFORE you close your current SSH session. Locking yourself out of your own server is the most common VPS hardening mistake.

Additional rules for common services:

bash
# MySQL/MariaDB (only from specific IP, not publicly)
ufw allow from 10.0.0.5 to any port 3306 comment 'MySQL from app server'

# Redis (only localhost, never public)
# Redis should bind to 127.0.0.1 in redis.conf — no UFW rule needed

# SMTP (only if running a mail server)
ufw allow 587/tcp comment 'SMTP submission'

fail2ban: Automated Intrusion Prevention

fail2ban monitors log files and automatically bans IP addresses that show malicious behavior — repeated failed SSH logins, 404 storms, authentication failures on web apps.

bash
apt-get install fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local:

ini
[DEFAULT]
# Ban for 1 hour
bantime = 3600
# Find pattern within 10 minutes
findtime = 600
# 5 failures trigger a ban
maxretry = 5
# Email notifications (optional)
destemail = your-email@domain.com
sendername = Fail2Ban
action = %(action_mwl)s

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log

[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
bash
systemctl enable fail2ban
systemctl start fail2ban

# Check ban status
fail2ban-client status sshd

CrowdSec: Collective Threat Intelligence

CrowdSec extends fail2ban's concept with a community-powered threat intelligence network. When one server in the CrowdSec network bans an IP for malicious behavior, that signal is shared with all participating servers. You benefit from the collective experience of millions of servers worldwide.

bash
# Install CrowdSec
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
apt-get install crowdsec

# Install the Nginx bouncer (blocks malicious IPs at the Nginx level)
apt-get install crowdsec-nginx-bouncer

# Check acquired scenarios
cscli scenarios list

# Add community blocklist subscription (free)
cscli bouncers add NginxBouncer

CrowdSec automatically detects and blocks: - SSH brute force - Web scanning (Nikto, SQLMap, Nmap) - Credential stuffing - CVE exploitation attempts

Check your current ban list:

bash
cscli decisions list

SSL/TLS with Let's Encrypt and Certbot

bash
apt-get install certbot python3-certbot-nginx

# Obtain certificate
certbot --nginx -d yourdomain.com -d www.yourdomain.com

# Verify auto-renewal
certbot renew --dry-run

# Check renewal timer
systemctl status certbot.timer

Certbot automatically modifies your Nginx configuration. After running certbot, verify that it has: - Added ssl_protocols TLSv1.2 TLSv1.3; - Added proper cipher configuration - Set up HTTPS redirect from port 80

If any of these are missing, apply the Nginx TLS hardening configuration from our Nginx hardening guide.


Automatic Security Updates

Unpatched software is a primary attack vector. Configure unattended upgrades to apply security patches automatically:

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

Edit /etc/apt/apt.conf.d/50unattended-upgrades:

Unattended-Upgrade::Automatic-Reboot 'false';
Unattended-Upgrade::Mail 'your-email@domain.com';
Unattended-Upgrade::Remove-Unused-Dependencies 'true';

Set to Automatic-Reboot "true" only if your application supports it (most web applications do, with proper process management).


Verifying Your Public Security Posture

After hardening your VPS, verify your public-facing security with external tools. Internal testing can miss issues that external scanners catch.

Run a ZeriFlow scan on your domain to verify: - HTTPS is correctly configured with a valid certificate - Security headers (HSTS, CSP, X-Frame-Options, etc.) are present - TLS version and cipher strength - HTTP to HTTPS redirect behavior - Mixed content issues

ZeriFlow's 80+ automated checks simulate what an attacker or security auditor would see from the outside — the ground truth of your server's security posture.


FAQ

### Q: Should I change the default SSH port? A: Changing the SSH port from 22 to a non-standard port (e.g., 2222 or a random high port) reduces automated brute force attempts dramatically — most scanners only target port 22. It is not security by obscurity in a meaningful sense (any port scanner will find it), but it eliminates an enormous amount of noise from your logs and reduces the risk window. Combined with key-only authentication, it is a worthwhile change.

### Q: Is fail2ban or CrowdSec better? A: They complement each other. fail2ban is reactive — it bans based on patterns in your local logs. CrowdSec is proactive — it blocks IPs that have attacked other servers in the community network before they attack yours. Run both for maximum protection.

### Q: How do I recover if I lock myself out via UFW? A: Most VPS providers (DigitalOcean, Linode, Hetzner, Vultr) offer a web-based console that connects to your server without SSH. Log in via the web console, disable UFW with ufw disable, fix your rules, and re-enable. This is why you should always test your SSH connection from a new terminal before closing your original session.

### Q: What else should I install for a production VPS? A: Consider: logwatch for daily log summaries, lynis for automated security audits (lynis audit system), rkhunter for rootkit detection, and a monitoring solution like Netdata or Prometheus + Grafana for performance and anomaly detection.


Conclusion

VPS security hardening is a checklist, not a single action. SSH key authentication, a properly configured firewall, fail2ban, CrowdSec, automatic security updates, and SSL together create a layered defense that eliminates the vast majority of automated attack vectors.

Apply this checklist on every new server you provision — not just production servers. Development and staging servers are often attacked and used as pivot points to reach production environments.

Verify your server's public security posture with ZeriFlow after completing this checklist. The free scan provides an outside-in view of exactly what your server exposes to the internet, which is the only perspective that matters for security.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading