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.
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:
- Set up a reverse proxy like Traefik or Nginx Proxy Manager for SSL and routing
- Configure regular backups — if something goes wrong, you want to recover
- Deploy Docker securely — never run containers as root
- 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:
| Step | What | Why |
|---|---|---|
| 1 | Choose a good provider | Security starts at the foundation |
| 2 | Create non-root user | Principle of least privilege |
| 3 | SSH key authentication | Mathematically unbreakable |
| 4 | Harden SSH config | Close the front door |
| 5 | UFW firewall | Block unauthorized access |
| 6 | Fail2ban | Auto-ban attackers |
| 7 | Automatic updates | Patch vulnerabilities quickly |
| 8 | Disable unused services | Reduce attack surface |
| 9 | Log monitoring | Know what’s happening |
| 10 | Regular maintenance | Security 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?
- How to Self-Host Everything — Now that your VPS is locked down, learn how to deploy your first self-hosted applications.
- 5 Apps You Should Self-Host Right Now — Practical recommendations on what to actually self-host (with setup guides).
- Coolify vs CapRover vs Dokku — Automate deployments with a platform built for self-hosters.
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.