How to Secure a Linux VPS with Fail2Ban: Block SSH Bruteforce Attacks

Learn how to protect your Linux VPS with Fail2Ban: installation, custom SSH jail, firewall integration, and monitoring of intrusion attempts.

Any Linux VPS with SSH exposed to the internet receives hundreds of bruteforce attempts per day. Automated bots scan the entire IPv4 space testing common combinations like root/admin, ubuntu/123456, and leaked password lists. Even if your password is strong, the noise in your logs, the CPU consumption, and the risk of a weak credential on some secondary user make these attempts a real problem.

Fail2Ban solves this by reading authentication logs in real time, identifying repeated failures from the same IP, and adding temporary firewall rules to block that IP. It is lightweight (under 50 MB of RAM in typical use), available in the official Debian and Ubuntu repositories, and works with any standard firewall.

This tutorial covers installation on Ubuntu/Debian, configuring a custom SSH jail, integrating with UFW, and fine-tuning progressive bans. Estimated time: 15-20 minutes.

Prerequisites

Prerequisites

A VPS running Ubuntu 22.04/24.04 LTS or Debian 12 with sudo access, an active SSH connection, and UFW (or another firewall) already configured. You also need your fixed administration IP — without it, there is a real risk of locking yourself out.

Distribution tested Ubuntu 24.04 LTS
Package fail2ban
Expected firewall ufw / nftables / iptables
Default SSH port 22

If you do not know the public IP you administer the VPS from, run curl -4 ifconfig.me on your local machine before starting. Write it down — you will need it.

Installing Fail2Ban

Installation is straightforward through the package manager. There is no need to compile anything or add external repositories.

01

Update the package index:

sudo apt update

This ensures you download the latest Fail2Ban version available in the official repositories.

02

Install the package:

sudo apt install -y fail2ban

The installer automatically enables and starts the service via systemd. The default configuration protects SSH with conservative parameters that you will override in the next step.

03

Verify that the service is running:

sudo systemctl status fail2ban

The output should show active (running) in green. If it shows failed, run sudo journalctl -u fail2ban -n 50 to see the error — it is almost always a permission issue on /var/log/auth.log (fix with sudo chmod 640 /var/log/auth.log).

Creating jail.local for SSH

Fail2Ban has two configuration files: jail.conf (the package default, which can be overwritten during updates) and jail.local (your customizations, preserved across updates). Never edit jail.conf — always create jail.local.

01

Create the local configuration file:

sudo nano /etc/fail2ban/jail.local
02

Paste the configuration below, replacing YOUR.IP.HERE with your administration IP:

[DEFAULT]
# IPs that are never banned (whitelist)
ignoreip = 127.0.0.1/8 ::1 YOUR.IP.HERE

# Ban duration: 1 hour
bantime  = 3600

# Observation window: 10 minutes
findtime = 600

# Attempts before banning
maxretry = 5

# Progressive ban (multiplies on repeat offenses)
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 604800

# Backend: systemd on modern distributions
backend = systemd

[sshd]
enabled  = true
port     = ssh
filter   = sshd
logpath  = %(sshd_log)s
maxretry = 5

Each parameter has a direct effect. bantime = 3600 keeps the IP banned for 1 hour on the first offense. With bantime.increment = true and bantime.factor = 2, the second offense bans for 2 hours, the third for 4 hours, and so on — up to the one-week cap defined in bantime.maxtime.

03

Save (Ctrl+O, Enter) and exit (Ctrl+X). Reload Fail2Ban:

sudo systemctl restart fail2ban
Watch out for ignoreip

If you administer the VPS from a dynamic residential IP (most home connections), ignoreip may fail when your IP changes. Consider using a CIDR range from your ISP or authenticating via SSH key (more robust than relying on IP whitelisting).

Integrating with UFW

By default, Fail2Ban uses iptables to enforce bans. If you use UFW (a friendlier interface on top of iptables), the blocks work but appear in a separate chain — which can confuse audits. The correct integration delegates blocking to UFW itself.

01

Edit /etc/fail2ban/jail.local and add to the [DEFAULT] block:

banaction = ufw
banaction_allports = ufw
02

Restart the service:

sudo systemctl restart fail2ban

Now the bans show up directly in sudo ufw status numbered as DENY rules at the top of the list — making inspection easier.

Verification

Confirming that Fail2Ban is actually active and monitoring SSH takes 30 seconds.

01

Check the SSH jail status:

sudo fail2ban-client status sshd

Expected output:

Status for the jail: sshd
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     0
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 0
   |- Total banned:     0
   `- Banned IP list:

If the jail is listed and File list points to the correct auth.log, it is working. After a few hours in production, Total failed and Total banned will naturally start to climb — bots from across the internet are already trying.

02

Forced test (optional, do it from a second IP, not from your admin IP): try 6 SSH logins with the wrong password. The 6th should fail with “Connection refused” and the IP should appear in Banned IP list.

Troubleshooting

Fail2Ban does not ban even after several failures

Most common cause: the backend is not reading the right log. On Ubuntu 22.04+ the auth.log was moved to journald and may not exist as a file. Confirm with ls -la /var/log/auth.log. If it does not exist, keep backend = systemd in jail.local — which is already in the example above.

Another cause: outdated regex filter. Run sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf and check whether “matched” returns a number > 0.

”Unable to start jail” in the log

Almost always permissions. Fail2Ban runs as the fail2ban user (or root on older distros) and needs to read /var/log/auth.log. Check with sudo -u fail2ban cat /var/log/auth.log | head — if access is denied, adjust the ACL.

I locked myself out and I do not have a static IP

Emergency access

If you lost SSH access and your administration IP is not in ignoreip, do NOT keep forcing logins — that will only extend the ban. Open the KVM console for your VPS through the Hostini panel, log in via the console, and run sudo fail2ban-client set sshd unbanip YOUR.IP to release it.

Next steps

Fail2Ban solves one layer of the problem — noisy bruteforce. For a complete security posture, also consider:

  • Disable password login: edit /etc/ssh/sshd_config setting PasswordAuthentication no after confirming your public key works. Bruteforcing keys is mathematically infeasible.
  • Change the default SSH port: a port other than 22 eliminates 95% of automated scanning (serious attackers will find it, but dumb bots will not). Remember to update UFW first.
  • Enable 2FA for SSH with Google Authenticator: adds a 6-digit TOTP to the authentication chain.
  • Monitor bans: configure email notifications in Fail2Ban (action = %(action_mwl)s) to receive details for each ban.
  • Extend to other services: if you expose Nginx or Postfix, enable the corresponding jails in the same jail.local.

If you are running critical applications in production, a Hostini VPS already includes edge DDoS protection — Fail2Ban remains useful for application-layer bruteforce, but volumetric attacks are filtered before they reach your machine’s kernel. See the plans at /vps.

Frequently asked questions

Does Fail2Ban replace a firewall like UFW or nftables?

No. Fail2Ban is a reactive layer that adds dynamic rules to the firewall already running on your VPS. It needs iptables, nftables, or UFW working in order to enforce bans. The correct combination is a firewall closing ports plus Fail2Ban responding to attempts on ports that must remain open (like SSH).

How long is an attacking IP banned by default?

By default it is 10 minutes (600 seconds). For production that is too short — bots simply wait and try again. In real environments increase bantime to 1 hour (3600) or enable bantime.increment=true for progressive bans: each repeat offense automatically multiplies the duration.

Can Fail2Ban lock me out of my own VPS?

Yes, if you mistype your SSH password 5 times from your IP. Solution: add your fixed IP (or corporate range) to ignoreip in jail.local. If you still get locked out, use the KVM console in the Hostini panel — it gives you a direct shell without going through SSH, even with the firewall active.

How do I see which IPs are currently banned?

Run `sudo fail2ban-client status sshd`. The 'Banned IP list' line shows the IPs currently blocked in the sshd jail. To unban manually: `sudo fail2ban-client set sshd unbanip 1.2.3.4`.

Does Fail2Ban work with SSH using public keys instead of passwords?

Yes, but the bruteforce risk drops drastically once you disable password authentication (PasswordAuthentication no in /etc/ssh/sshd_config). In that scenario Fail2Ban is still useful for blocking noisy scanners that flood your logs and consume CPU even though they cannot get in.

Can I use Fail2Ban to protect services other than SSH?

Yes. Fail2Ban ships with ready-to-use filters for Nginx, Apache, Postfix, Dovecot, vsftpd, and others. You enable each one by adding a [jail-name] section with enabled = true in jail.local. The principle is the same: it reads the service log, matches a failure regex, and adds a firewall rule.

Topics:
Next steps Ryzen cloud with NVMe storage and always-on DDoS protection.Go live on a Hostini VPS →
Was this tutorial helpful?
Chat on WhatsApp