How to Secure Your VPS: Essential Security Hardening for Self-Hosters

How to Secure Your VPS: Essential Security Hardening for Self-Hosters

Learn how to harden your VPS with SSH keys, firewalls, Fail2ban, and automatic updates. A complete security guide for self-hosting beginners.

đź’ˇ Disclosure: This article contains affiliate links. If you make a purchase through these links, we may earn a small commission at no extra cost to you. This helps support the site and keeps the content free.

You just spun up a shiny new VPS. You’ve got big plans — self-hosting your apps, taking control of your data, maybe running a few services for friends and family. There’s just one problem: the internet is absolutely brutal, and your fresh server is already under attack.

Within minutes of going online, your VPS will start receiving brute-force SSH attempts. I’m talking hundreds of bots scanning every IP range, every port, looking for weak passwords and misconfigured defaults. If you don’t secure your server, it’s only a matter of time before someone breaks in. I learned this the hard way during my first month of self-hosting—woke up to a cryptocurrency miner burning CPU cycles on my Hetzner bill.

The good news? Basic security hardening takes about 30 minutes and will stop 99% of automated attacks. This guide walks you through every step.

Why VPS Security Matters for Self-Hosters

Self-hosting gives you control and privacy. But here’s the trade-off: you’re now the security team. Unlike managed services where someone else sweats the details, your VPS is your responsibility.

A compromised server becomes a liability:

  • Spam relay — Your IP gets blacklisted, suddenly your emails won’t deliver anywhere
  • Crypto mining — Attackers rent your CPU to mine coins while your monthly bill skyrockets
  • Botnet node — Your server attacks other systems, and you’re legally liable
  • Data breach — Every password, file, and secret you’re hosting is now exposed

I’ve seen homelabbers get their ISP shut down because their “secure” server became a spam hub. Not fun.

The stakes are real. Let’s fix this.


Step 1: Choose a Reputable VPS Provider

Security starts before you even get your login credentials. Your provider’s infrastructure matters more than you’d think.

Look for these red flags:

  • No DDoS protection included — You’ll get hammered if you ever go slightly popular
  • Broken firewall tools — Some cheap providers make it painful to lock things down
  • No security certifications — ISO 27001 or similar tells you they actually care
  • Slow patch schedules — If their hypervisors run unpatched kernels for months, run
  • No 2FA on accounts — If someone compromises your provider account, they own your server

Providers I’d actually recommend:

  • Hetzner — EU-based, great price-to-performance, takes privacy seriously
  • DigitalOcean — Not the cheapest, but their UI and docs are solid
  • Vultr — Global locations, pay-per-hour is nice for testing
  • Hostinger VPS — Budget-friendly if you’re just starting out

First thing you do: Enable 2FA on your provider account. Right now. If an attacker gets into your Hetzner dashboard, they can nuke your entire server before you even notice.


Step 2: Create a Non-Root User

This one’s non-negotiable: never run services as root, never log in as root over SSH.

I know some guides skip this, and they’re wrong. Create a dedicated user:

# As root, create a new user
adduser deploy

# Add to sudo group (Debian/Ubuntu)
usermod -aG sudo deploy

# Or for CentOS/RHEL
usermod -aG wheel deploy

Pick a strong password. Then switch:

su - deploy

From now on, you’ll use sudo when you need elevated privileges. This creates an audit trail (you can see who ran what) and prevents you from accidentally rm -rf / the entire server.


Step 3: Set Up SSH Key Authentication

Password authentication is vulnerable to brute-force attacks. SSH keys are mathematically secure and way more convenient. Here’s how to set them up.

Generate a Key Pair (on your local machine)

# On your laptop/desktop, NOT on the server
ssh-keygen -t ed25519 -C "[email protected]"

Press Enter to accept the default location (~/.ssh/id_ed25519). Add a passphrase for extra security.

Copy the Public Key to Your Server

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

Or if that doesn’t work:

# On your local machine, copy your public key
cat ~/.ssh/id_ed25519.pub

Copy the output. On your server:

mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "your-public-key-here" >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Test It

Open a new terminal and try:

ssh deploy@your-server-ip

If you get in without a password prompt, it worked!


Step 4: Harden SSH Configuration

Now let’s lock down SSH itself. This is where most attacks concentrate, so we’re going to tighten the screws.

Edit /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

Make these changes:

# Disable root login entirely
PermitRootLogin no

# Keys only — no password fallback
PasswordAuthentication no

# Change the default port (optional, but I recommend it)
Port 2222

# Fail hard and fast
MaxAuthTries 3

# No empty password accounts
PermitEmptyPasswords no

# Only SSH v2
Protocol 2

# Kick out idle sessions
ClientAliveInterval 300
ClientAliveCountMax 0

Critical: Before you restart SSH, test it in a new terminal. If you break this, you lock yourself out:

ssh -p 2222 deploy@your-server-ip

If it works, restart SSH:

sudo systemctl restart sshd

Then keep that test terminal open while you update your local SSH config:

# ~/.ssh/config on your local machine
Host myserver
    HostName your-server-ip
    User deploy
    Port 2222
    IdentityFile ~/.ssh/id_ed25519

Now you just type ssh myserver and you’re in. Easy.


Step 5: Configure the Firewall (UFW)

A firewall blocks the stuff you didn’t invite. UFW makes it stupidly simple on Ubuntu/Debian.

sudo apt update
sudo apt install ufw

Set Up Basic Rules

# Deny everything inbound by default
sudo ufw default deny incoming

# Allow everything outbound by default
sudo ufw default allow outgoing

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

# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

Enable It

sudo ufw enable

Warning: If you haven’t allowed SSH yet, you’ll lock yourself out. Make sure SSH is in the allow list first.

Check what you’ve configured:

sudo ufw status verbose

Should look like:

Status: active

To                         Action      From
--                         ------      ----
2222/tcp                   ALLOW       Anywhere
80/tcp                     ALLOW       Anywhere
443/tcp                    ALLOW       Anywhere

Adding Rules Later

When you deploy new services, add their ports:

# Example: Nextcloud on 8080
sudo ufw allow 8080/tcp

# Example: Only allow a specific IP to access something
sudo ufw allow from 203.0.113.0/24 to any port 3000

Step 6: Install Fail2ban

Fail2ban is your bouncer. It watches the log files and bans IPs that act like jerks (repeated failed login attempts). Essential.

sudo apt install fail2ban

Create a local configuration (don’t edit the default config, it gets overwritten):

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Find these settings and modify them:

[DEFAULT]
# Ban for 1 hour after 5 failed attempts
bantime = 3600
findtime = 600
maxretry = 5

# Optional: send you an email when stuff gets banned
destemail = [email protected]
action = %(action_mwl)s

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

Start it:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Check how many attackers you’ve blocked:

sudo fail2ban-client status sshd

After a few hours, you’ll see pages of banned IPs. It’s weirdly satisfying watching the bots get blocked in real-time.


Step 7: Enable Automatic Security Updates

Security patches drop all the time. You can’t manually check every package on every server. Enable automatic updates for critical stuff.

Ubuntu/Debian

sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

Select “Yes” when prompted. The system now auto-patches critical security fixes.

To customize what gets updated:

sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

CentOS/RHEL

sudo dnf install dnf-automatic
sudo systemctl enable --now dnf-automatic.timer

Step 8: Disable Unnecessary Services

Every running service is a potential attack vector. Audit what’s actually running:

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

Disable anything you don’t need:

# Printing service (not needed on servers)
sudo systemctl disable cups

# Local network discovery (who needs it?)
sudo systemctl disable avahi-daemon

# Bluetooth (obviously not needed)
sudo systemctl disable bluetooth

Step 9: Set Up Log Monitoring

Logs are your paper trail. Make sure you’re actually looking at them.

Check Important Logs

# Authentication attempts
sudo tail -f /var/log/auth.log

# System messages
sudo tail -f /var/log/syslog

# Just the failures
sudo grep "Failed password" /var/log/auth.log | tail -20

Consider a Log Aggregator

For serious self-hosters, these tools save your life:

  • Loki + Grafana — Lightweight log aggregation with dashboards that actually look nice
  • Uptime Kuma — Self-hosted monitoring with alerts

We have a guide on setting up Uptime Kuma in our recommended apps article.


Step 10: Regular Maintenance Checklist

Security isn’t a “set it and forget it” situation. Here’s what I check regularly:

Weekly

  • Check Fail2ban: sudo fail2ban-client status
  • Review auth logs: sudo grep "Failed password" /var/log/auth.log
  • Check disk space: df -h

Monthly

  • Full system update: sudo apt update && sudo apt upgrade
  • Review what services are running: sudo systemctl list-units --type=service
  • Audit users: cat /etc/passwd | grep -v nologin
  • Check for rootkits: sudo apt install rkhunter && sudo rkhunter --check

Quarterly

  • Rotate SSH keys
  • Review firewall rules: sudo ufw status verbose
  • Update your documentation
  • Test your backup restoration (seriously, do this)

Bonus: Security Quick Wins

A few more hardening tricks that take seconds:

Disable IPv6 (if you’re not using it)

sudo nano /etc/sysctl.conf

Add:

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

Apply: sudo sysctl -p

Secure Shared Memory

sudo nano /etc/fstab

Add:

tmpfs     /run/shm     tmpfs     defaults,noexec,nosuid     0     0

Set Up a MOTD Warning

sudo nano /etc/motd

Add something like:

***************************************************************************
                          AUTHORIZED ACCESS ONLY
This system is monitored. Unauthorized access will be prosecuted.
***************************************************************************

Won’t stop real attackers, but it establishes legal notice.


What’s Next?

You’ve done the hard work. Your VPS is now significantly more secure than 90% of servers out there. But security is never “done” — it’s an ongoing process.

Next things to consider:

  1. Set up a reverse proxy like Traefik or Nginx Proxy Manager for SSL and routing
  2. Configure regular backups — if something goes wrong, you want to recover
  3. Deploy Docker securely — never run containers as root
  4. Consider Wireguard VPN for accessing admin panels from anywhere

If you’re new to self-hosting, check out our Complete Beginner’s Guide for setting up your first services on this now-secure foundation.


Summary

Here’s what we covered:

StepWhatWhy
1Choose a good providerSecurity starts at the foundation
2Create non-root userPrinciple of least privilege
3SSH key authenticationMathematically unbreakable
4Harden SSH configClose the front door
5UFW firewallBlock unauthorized access
6Fail2banAuto-ban attackers
7Automatic updatesPatch vulnerabilities quickly
8Disable unused servicesReduce attack surface
9Log monitoringKnow what’s happening
10Regular maintenanceSecurity is ongoing

Total time: About 30-45 minutes Peace of mind: Priceless

Your self-hosted services deserve a secure foundation. You’ve got one now.


FAQ: VPS Security & Hardening

How often should I update my VPS?

Weekly for critical security patches. Monthly for minor updates. Most hardening strategies assume you’re keeping up with updates.

What’s the difference between UFW and iptables?

UFW is a simplified interface on top of iptables. Both do the same thing; UFW is easier to manage. Use UFW unless you have advanced needs.

Do I really need Fail2ban?

Yes. Brute-force attacks are constant and relentless. Fail2ban blocks attackers after repeated failed attempts. It’s one of the most important layers.

Is SSH key authentication really necessary?

Absolutely. Password auth is a ticking time bomb. SSH keys are mathematically unbreakable and only add ~1 minute of setup. No downside.

Can I use a password AND an SSH key?

Yes. Setting PasswordAuthentication no requires SSH keys, but you can also set PubkeyAuthentication yes and force key-only auth. That’s the safest option.

What if I lose my SSH private key?

You can’t log in. Always back up your private key (somewhere secure) and consider keeping your VPS provider’s console access enabled as an emergency backup.

Does disabling root login break anything?

Nope. As long as your normal user has sudo access, everything works. Most tools assume sudo, not root login.

Is Fail2ban enough to stop all attacks?

No. Fail2ban stops brute-force attempts, but it’s one layer. You still need SSH hardening, firewalls, and regular updates. It’s defense in depth.

Can I check if I’ve been compromised?

Look for: unauthorized SSH keys, unfamiliar cron jobs, weird processes running. Install Lynis for a security audit: sudo apt install lynis && sudo lynis audit system.

How often should I rotate my SSH keys?

Every 1-2 years as a best practice. Sooner if you suspect compromise.

What happens if I block the wrong port?

You lose access. Use the VPS provider’s console to recover. This is why testing before you restart SSH is crucial.


Next Steps

Ready to move beyond just security?


Stay in the loop 📬

Get self-hosting tutorials, tool reviews, and infrastructure tips delivered to your inbox. No spam, unsubscribe anytime.

Join 0 self-hosters. Free forever.