
Any VPS with a public IP faces SSH brute force attacks around the clock. Bots from around the world scan for Port 22 and try millions of username/password combinations every day. Even with a strong password, these attempts waste CPU resources and flood your auth.log with noise, making it hard to spot real incidents.
Fail2Ban is designed to solve this problem directly. It monitors your log files in real time and automatically bans IPs that exceed your login failure threshold. This guide walks you through the complete setup on Ubuntu 22.04.
Prerequisites: An Ubuntu 20.04/22.04 VPS with root or sudo access, reachable via SSH.
What Is an SSH Brute Force Attack?
A brute force attack means trying every possible username/password combination: root/123456, admin/password, or credentials leaked from previous data breaches. Even if your password is uncrackable, the constant flood of requests consumes server resources and pollutes your logs.
How Fail2Ban Works
Fail2Ban is an Intrusion Prevention Framework written in Python. It operates in three stages:
- Monitor: Reads log files such as
/var/log/auth.login real time. - Detect: Matches lines against filter patterns (e.g., "Failed password for root from 1.2.3.4").
- Act: Adds an iptables or nftables rule to block that IP temporarily.
Once an IP is banned, all traffic from that source is dropped at the firewall. Fail2Ban automatically unbans the IP after the configured ban duration (default: 10 minutes).
Step 1 — Update System and Install Fail2Ban
sudo apt update && sudo apt upgrade -y sudo apt install fail2ban -y
Verify the installation and confirm it is running:
sudo systemctl status fail2ban
If you see Active: active (running), Fail2Ban is already protecting your server with default settings.
Enable autostart on reboot:
sudo systemctl enable fail2ban
Step 2 — Understand the Config Structure
Fail2Ban uses two key config files:
/etc/fail2ban/jail.conf— Default package config. Never edit this directly; it will be overwritten on package upgrades./etc/fail2ban/jail.local— Your custom overrides. Create this file yourself.
Best Practice: Always edit jail.local only. Leave jail.conf untouched so your config survives package updates.
Step 3 — Create and Configure jail.local
Create the file:
sudo nano /etc/fail2ban/jail.local
Paste the following config and adjust the values to your needs:
[DEFAULT] # Ban an IP that fails login 5 times within 10 minutes bantime = 3600 # Ban duration: 1 hour (seconds) findtime = 600 # Detection window: 10 minutes maxretry = 5 # Failures allowed before ban # IPs to never ban (your own machine) ignoreip = 127.0.0.1/8 ::1 [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 3 # Stricter for SSH — 3 strikes and you're banned bantime = 7200 # Ban for 2 hours
⚠️ Important: If you changed your SSH port, specify it explicitly, e.g. port = 2222 instead of port = ssh. Otherwise Fail2Ban won't monitor the port you're actually using.
Save the file with Ctrl+O and exit with Ctrl+X, then restart Fail2Ban:
sudo systemctl restart fail2ban
Step 4 — Check Status and Active Jails
View a summary of all Fail2Ban jails:
sudo fail2ban-client status
View details for the SSH jail specifically:
sudo fail2ban-client status sshd
The output shows the number of currently banned IPs and the total IPs ever banned.
Step 5 — Unban an IP
If a legitimate user gets accidentally banned, unban them immediately:
sudo fail2ban-client set sshd unbanip 1.2.3.4
Replace 1.2.3.4 with the actual IP address.
Prevention tip: Add your own IP to the ignoreip line in jail.local so you can never accidentally lock yourself out.
Step 6 — Monitor the Log
Fail2Ban logs every ban and unban action to:
sudo tail -f /var/log/fail2ban.log
Example log output showing bans being applied:
2026-05-11 14:32:01,782 fail2ban.actions [12345]: NOTICE [sshd] Ban 185.22.xxx.xxx 2026-05-11 14:32:01,783 fail2ban.actions [12345]: NOTICE [sshd] Ban 91.108.xxx.xxx
Confirm Fail2Ban is picking up actual brute force attempts:
sudo grep "Failed password" /var/log/auth.log | tail -20
Bonus: Recidive Jail — Long-Term Bans for Repeat Offenders
The Recidive jail bans IPs for a full week if they keep getting banned repeatedly. Add this to jail.local:
[recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = 604800 # 1 week findtime = 86400 # Look back 1 day maxretry = 3 # Banned 3 times in 1 day → 1-week ban
Add Jails for Nginx, Apache, and WordPress
Fail2Ban protects far more than SSH. If your VPS runs a web server like Nginx or Apache, you can add jails to block IPs that launch Layer 7 attacks: brute force against the WordPress login, rapid 404 probing to scan for vulnerabilities, or bad bots that scrape aggressively and waste bandwidth.
Nginx Jail (HTTP Auth + Bad Bot)
Add the following to /etc/fail2ban/jail.local for VPS servers running Nginx:
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
bantime = 3600
[nginx-botsearch]
enabled = true
filter = nginx-botsearch
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 600
bantime = 7200 # Block bots scanning for vulnerable pathsApache Jail
If your VPS uses Apache (for example, with DirectAdmin), use the apache-* filter family instead:
[apache-auth]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/apache2/error.log
maxretry = 5
bantime = 3600
[apache-badbots]
enabled = true
port = http,https
filter = apache-badbots
logpath = /var/log/apache2/access.log
maxretry = 2
bantime = 86400 # Block bad bots for 1 dayWordPress Jail (wp-login.php / xmlrpc.php)
WordPress is a top brute force target via wp-login.php and xmlrpc.php. Fail2Ban has no built-in WordPress filter, so create one at /etc/fail2ban/filter.d/wordpress.conf:
[Definition]
failregex = ^<HOST> .* "POST /wp-login.php
^<HOST> .* "POST /xmlrpc.php
ignoreregex =Then add a jail referencing this filter in jail.local (set logpath to match your domain's access log):
[wordpress] enabled = true filter = wordpress port = http,https logpath = /var/log/nginx/access.log maxretry = 5 findtime = 300 bantime = 7200
⚠️ Watch out: If your site sits behind Cloudflare or a reverse proxy, the IP in the log will be the proxy's, not the real visitor's. Configure Nginx/Apache to log X-Forwarded-For first, otherwise Fail2Ban will ban Cloudflare's IPs instead of the attacker.
After adding any new jail, always reload and confirm the filter has no syntax errors:
sudo fail2ban-client reload sudo fail2ban-client status
Inspect and Manage Bans with fail2ban-client
Once you have several jails running, you need the fail2ban-client commands to get an overview and manage banned IPs. Here are the essentials.
List all active jails:
sudo fail2ban-client status
View details of a specific jail, including the IPs currently banned:
sudo fail2ban-client status wordpress
The table below summarizes the management commands you'll use most:
| Command | What it does |
|---|---|
fail2ban-client status |
List all running jails |
fail2ban-client status sshd |
Show banned IPs in the sshd jail |
fail2ban-client set sshd unbanip IP |
Unban the specified IP |
fail2ban-client set sshd banip IP |
Manually ban an IP immediately |
fail2ban-client reload |
Reload config without stopping the service |
To quickly count the total number of bans applied, grep the log files:
sudo zgrep "Ban" /var/log/fail2ban.log* | wc -l
This command includes compressed (.gz) rotated logs, giving you the full picture of how many attacks have been blocked since you started.
Whitelisting IPs and Common Problems
The headache that haunts every admin is "banning yourself" and getting locked out. Setting a correct whitelist (ignoreip) is just as important as the ban rules. Add your home/office IP and any trusted internal ranges to jail.local:
[DEFAULT]
# Separate each IP/range with a space — supports IPv4 and IPv6
ignoreip = 127.0.0.1/8 ::1 203.0.113.45 198.51.100.0/24After editing ignoreip, always run sudo systemctl restart fail2ban for the change to take effect. The table below lists common problems and their fixes:
| Symptom | Cause / Fix |
|---|---|
| Fail2Ban never bans any IP | logpath points to the wrong file, or SSH uses a port not declared in the jail |
| Suddenly can't SSH in | You banned yourself — use your provider's console, then unbanip and add ignoreip |
| Banning Cloudflare IPs | Logs record the proxy IP — configure real_ip / X-Forwarded-For in the web server |
| Newer Ubuntu has no auth.log | Some Ubuntu releases use journald — set backend = systemd in the jail |
Lock-out prevention tip: If you use a dynamic home IP that changes often, set up SSH key authentication and disable password login entirely. It is far more secure and removes the worry of SSH brute force altogether.
Setup Checklist
- Install Fail2Ban and enable autostart.
- Create
/etc/fail2ban/jail.local— never editjail.conf. - Configure
[sshd]jail with the correct port number. - Add your own IP to
ignoreipto avoid locking yourself out. - Verify with
fail2ban-client status sshd. - Optionally enable the Recidive jail for persistent attackers.
Frequently Asked Questions About Fail2Ban
How is Fail2Ban different from a firewall (UFW/iptables)?
A firewall like UFW or iptables sets static rules for which ports are open or closed. Fail2Ban is a dynamic system that reads logs and instructs the firewall to block IPs showing attack behavior, then unbans them when the timer expires. They work together — Fail2Ban adds temporary iptables/nftables rules for you. You don't choose one over the other; you should run both.
Does Fail2Ban use a lot of VPS resources?
Very little. Fail2Ban is a Python daemon that tails log files, using roughly 20-40 MB of RAM with negligible CPU impact on a typical VPS. Even AsiaGB's entry-level VPS from 500 THB/month runs it comfortably without affecting your sites or apps — unless you set a very wide findtime over multi-gigabyte logs, which is uncommon.
If I change my SSH port, do I still need Fail2Ban?
You still should. Changing the port greatly reduces bots that scan Port 22 directly, but it doesn't stop bots that scan all ports or target you specifically. Fail2Ban still catches and bans IPs that find your port — just remember to update port = ssh in the jail to your new port, or Fail2Ban will monitor the wrong one.
Are banned IPs unbanned automatically or do I do it manually?
They are unbanned automatically once the configured bantime elapses — for example, 7200 seconds blocks the IP for 2 hours, then releases it. You can unban early with fail2ban-client set [jail] unbanip [IP], and setting bantime = -1 creates a permanent ban that lasts until you remove it manually.
VPS with Full Root Access for Maximum Security Control
Configure every security setting yourself. Linux VPS starting at 500 THB/month — Thailand or Singapore.
View VPS Plans