Securing a VPS: Firewalls, Fail2ban, and Automatic Updates
Harden a Linux VPS on DigitalOcean, Hetzner, or BinaryLane with a provider firewall and ufw, key only SSH, Fail2ban jails for public services, and unattended security updates that keep packages patched.
A fresh VPS gets its first SSH login attempt from a stranger within minutes of receiving a public IP. That is not a targeted attack, it is the background noise of the internet, and a server that is not prepared for it will eventually be added to someone’s botnet. This guide covers the three layers that stop the noise from becoming an incident on DigitalOcean, Hetzner Cloud, BinaryLane, or any other Linux VPS: a firewall at the provider edge and on the host, Fail2ban to ban repeat offenders, and automatic security updates so that known vulnerabilities are patched while you sleep.
Two firewalls are better than one
Most VPS providers offer a network firewall that filters traffic before it reaches the server. DigitalOcean calls it Cloud Firewalls and applies rules to Droplets by tag or name. Hetzner Cloud Firewalls attach to servers in a project and support both inbound and outbound rules. Where a provider offers this, use it as the outer layer: allow SSH from your own address ranges, allow 80 and 443 to the world if the server hosts a website, and deny everything else. Because the rules are enforced outside the machine, a misconfigured service or a compromised process on the host cannot open a port you did not allow.
The host firewall is the inner layer, and on Ubuntu and Debian the simplest tool is ufw. A sensible starting policy is deny incoming, allow outgoing, allow OpenSSH, and allow the web ports you need. Enable it only after the SSH rule exists, or you will lock yourself out. On RHEL derived images such as Rocky Linux and AlmaLinux, firewalld plays the same role with zones and services. Both are front ends for nftables or iptables, so if you prefer to write rules directly, that works too.
Harden SSH before anything else
SSH is the service every attacker probes first, and the fix is to remove passwords from the equation. Attach your public key at server creation time, confirm you can log in with it, and then set PasswordAuthentication to no and PermitRootLogin to prohibit-password or no in sshd_config. Create a regular user with sudo rights for daily work. Restarting sshd after the change applies it to new connections only, so keep your existing session open while you test a second one.
Moving SSH to a non standard port reduces log noise but does not add security, since port scanners find it within a day. Restricting SSH to your own IP range in the provider firewall is a far stronger control, and a small jump host or a VPN into the provider’s private network is stronger still. Key management, rotation, and hardware backed keys are covered in SSH keys for cloud servers.
Fail2ban for the services that must stay public
Fail2ban watches log files for repeated failures from the same address and adds a temporary firewall rule to drop that address. It ships with filters for sshd, Postfix, Dovecot, Nginx, Apache, and many other daemons. Install it from the distribution repositories, then create a jail.local file rather than editing jail.conf, because package upgrades overwrite the latter.
The settings worth understanding are few. maxretry is the number of failures allowed inside findtime before a ban; bantime is how long the ban lasts. The sshd jail is enabled by default on most distributions; enable additional jails only for services you actually run and whose logs Fail2ban can read. Add your own office or home address to ignoreip so that a typo in your password does not lock you out. The recidive jail bans addresses that have been banned repeatedly for a much longer period and is worth enabling on any server that has been public for more than a few weeks.
Fail2ban is a rate limiter, not a substitute for the SSH hardening above. With password authentication disabled, a banned attacker was never going to get in anyway; the benefit is a cleaner log and less CPU spent rejecting connections. On a mail server or a web application with its own login form, where password authentication is unavoidable, it is the main defense against credential stuffing.
Automatic security updates
Unpatched packages are how most VPS compromises actually happen, and the fix is to let the operating system apply security updates on its own. On Ubuntu and Debian, the unattended-upgrades package does this. It is preinstalled on Ubuntu Server images from DigitalOcean, Hetzner, and most other providers, but check that it is enabled and that the origins pattern includes the security repository. Configure it to remove unused kernel packages so /boot does not fill up, and decide whether it may reboot automatically. Automatic reboots at a fixed off peak hour are fine for a single stateless web server; for a database host you will want to schedule the reboot yourself after a kernel update.
On Rocky Linux, AlmaLinux, and other RHEL derivatives, dnf-automatic with the security upgrade type provides the same behavior through a systemd timer. Whichever tool you use, confirm it is running by checking the log after the next security release. A monitoring script that alerts when the server has needed a reboot for more than a few days is a small addition that catches the case where the update landed but the running kernel is still the old one.
| Layer | Tool | What it protects against | Check it with |
|---|---|---|---|
| Provider firewall | DigitalOcean Cloud Firewalls, Hetzner Cloud Firewalls, or equivalent | Exposure of ports you never intended to open | External port scan from another host |
| Host firewall | ufw, firewalld, nftables | Services listening on all interfaces by default | ufw status verbose, ss -tlnp |
| SSH hardening | sshd_config, keys only | Password guessing | sshd -T to print effective settings |
| Rate limiting | Fail2ban | Repeated login attempts, log noise | fail2ban-client status sshd |
| Patching | unattended-upgrades, dnf-automatic | Known vulnerabilities in installed packages | Update logs, reboot required flag |
What to check monthly
- Run a port scan against the public IP from outside and compare the result with the firewall rules you expect.
- Review the list of sudo users and authorized_keys files and remove anyone who has left the project.
- Read the Fail2ban ban log for services other than SSH; a jump in web login failures often precedes a real intrusion attempt.
- Confirm the last automatic update ran and that no reboot has been pending for weeks.
- Take a snapshot before any manual kernel or major package upgrade, since restoring a snapshot is faster than repairing a boot failure on a VPS.
If the server also runs on a private network with other hosts, the same firewall discipline applies inside the VPC; the guide to cloud networking basics explains how private interfaces and floating IPs change the picture.
Where to go next
Everything above works the same on any Linux VPS, so the choice of provider comes down to location, price, and account limits. The marketplace lists DigitalOcean accounts with three and ten Droplet limits, Hetzner accounts with five and ten server limits, and BinaryLane cloud accounts for Australian data centres. Whichever you choose, apply the firewall, SSH, and update steps before the server takes its first real traffic.
The first cloud server checklist walks through the creation steps that come before this guide, and the full security series is in the Cloud Library.