fail2ban Setup for SSH: Brute-Force Protection 2026
Run on the server with one command (check the script contents below first):
curl -sSL https://cp.lv/scripts/nastrojka-fail2ban.sh | sudo bash…or with wget:
wget -qO- https://cp.lv/scripts/nastrojka-fail2ban.sh | sudo bashScript contents nastrojka-fail2ban.sh:
#!/usr/bin/env bash
# Install fail2ban and set up the SSH jail (Ubuntu/Debian)
set -euo pipefail
# 1. Install
sudo apt update && sudo apt install -y fail2ban
# 2. Local configuration (quoted 'EOF' writes the sections verbatim)
sudo tee /etc/fail2ban/jail.local > /dev/null << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1w
[sshd]
enabled = true
port = ssh
backend = systemd
maxretry = 4
bantime = 2h
EOF
# 3. Apply and verify
sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd
echo "Done: fail2ban now protects SSH from brute force."
fail2ban protects a server from password guessing: it reads the logs, spots repeated failed logins and blocks the offending IP through the firewall. Setting it up comes down to installing the package and creating a jail.local file with the SSH jail enabled. Below is a working configuration.
How it works
fail2ban watches log files (SSH login attempts, for example), matches lines against filters (regular expressions) and, once the failure limit is exceeded, adds a rule to iptables/nftables that blocks the offender for a set period.
Step 1. Installation
apt update
apt install -y fail2ban
systemctl enable --now fail2ban
On CentOS/AlmaLinux: dnf install -y fail2ban.
Step 2. Local configuration
Never edit jail.conf directly — it is overwritten on updates. Open /etc/fail2ban/jail.local in an editor (sudo nano /etc/fail2ban/jail.local) and paste the contents:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = ssh
maxretry = 4
bantime = 2h
Or create the whole file with one command — copy it and paste it into the console (the quoted 'EOF' writes the sections verbatim):
sudo tee /etc/fail2ban/jail.local > /dev/null << 'EOF'
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = ssh
maxretry = 4
bantime = 2h
EOF
What the parameters mean:
maxretry— how many failures trigger a ban;findtime— the time window in which failures are counted;bantime— how long the ban lasts (-1means forever);ignoreip— the allow list (add your home IP so you never lock yourself out).
If SSH runs on a non-standard port, put the port number in place of ssh:
[sshd]
enabled = true
port = 2222
Step 3. Applying the config
systemctl restart fail2ban
fail2ban-client status
The output lists the active jails. Details for SSH:
fail2ban-client status sshd
This shows the number of attempts found and the currently banned IP addresses.
Progressive bans for repeat attacks
To make persistent offenders wait longer every time, enable incremental bans in [DEFAULT]:
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 1w
Each repeated ban of the same IP doubles, up to a maximum of one week.
Managing bans
Unban a specific IP:
fail2ban-client set sshd unbanip 203.0.113.10
Ban one manually:
fail2ban-client set sshd banip 203.0.113.10
Watch fail2ban’s own log:
tail -f /var/log/fail2ban.log
Working with systemd logs
On modern distributions SSH writes to journald rather than /var/log/auth.log. Make sure the backend reads journald by adding this to [sshd]:
backend = systemd
Check that the filter actually matches entries:
fail2ban-regex "systemd-journal[sshd]" /etc/fail2ban/filter.d/sshd.conf
The recidive jail
A separate recidive jail watches fail2ban’s own log and hands out long bans to IP addresses that keep coming back. Add this to jail.local:
[recidive]
enabled = true
logpath = /var/log/fail2ban.log
bantime = 1w
findtime = 1d
maxretry = 5
Now an address banned five times in one day is banned for a week.
Email notifications
fail2ban can send an email on every ban. In [DEFAULT] set:
destemail = [email protected]
sender = [email protected]
action = %(action_mwl)s
action_mwl sends a message with the log lines and a whois lookup of the offender. To deliver mail, the server needs a working MTA (postfix, for example).
Protecting the web server
Besides SSH, fail2ban also protects nginx. Here is a jail against authentication brute force:
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 5
Ready-made filters live in /etc/fail2ban/filter.d/ and are enabled through jail.local the same way.
Measuring the effect
How many addresses have been banned in total:
grep "Ban" /var/log/fail2ban.log | wc -l
The top banned IP addresses:
grep "Ban " /var/log/fail2ban.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head
A report like this shows at a glance that the protection is actually stopping attacks.
Frequently asked questions
Will I lock myself out?
Add your static IP to ignoreip. If you do get banned anyway, unban yourself through the VPS console (KVM) rather than over SSH.
Does fail2ban work with nftables?
Yes. Set banaction = nftables-multiport in [DEFAULT] if the server uses nftables.
Can it protect services other than SSH?
Yes, there are ready jails for nginx, postfix, dovecot and others — enable them in jail.local the same way.
How do I confirm a jail really works?
Look at fail2ban-client status sshd and /var/log/fail2ban.log — both show the detected and banned addresses.
How much does it consume? Very little — it is a lightweight Python daemon reading logs. There is no noticeable load on a VPS.
fail2ban is baseline protection for any server. For projects where security matters, take a VPS for security and VPN or a standard VPS plan. Leave the configuration to Server administration.