$linuxjunkies
>

How to Install and Configure fail2ban

Install fail2ban, configure the SSH jail, tune ban times, write custom filters, and verify bans are working — on Debian, Fedora, and Arch.

IntermediateUbuntuDebianFedoraArch9 min readUpdated June 7, 2026

Before you start

  • Root or sudo access on the target system
  • A running SSH daemon (sshd) if following the SSH jail steps
  • One of nftables, firewalld, ufw, or iptables active and functioning
  • Basic familiarity with editing files in a terminal

fail2ban watches your log files and temporarily bans IP addresses that show signs of brute-force or abuse. It does this by parsing logs with regex-based filters, then feeding matching IPs into a firewall action — by default a temporary ban. It won't replace a proper firewall or key-based SSH auth, but it's a solid second layer that cuts noise and real attacks alike. This guide installs fail2ban, wires up the SSH jail, tunes ban times, and shows you how to write a custom filter from scratch.

Installation

Debian / Ubuntu

sudo apt update && sudo apt install fail2ban -y

Fedora / RHEL / Rocky

sudo dnf install epel-release -y   # RHEL/Rocky only
sudo dnf install fail2ban -y

Arch Linux

sudo pacman -S fail2ban

After installation, enable and start the service:

sudo systemctl enable --now fail2ban

Understanding the Configuration Layout

fail2ban splits configuration into two layers. Files ending in .conf are the upstream defaults — never edit them directly, because package updates will overwrite your changes. Files ending in .local override the defaults and survive updates.

  • /etc/fail2ban/fail2ban.confdaemon-level settings (log level, socket path)
  • /etc/fail2ban/jail.conf — all jail defaults and bundled jail definitions
  • /etc/fail2ban/jail.localyour overrides; create this file
  • /etc/fail2ban/filter.d/ — filter definitions (regex patterns)
  • /etc/fail2ban/action.d/ — ban/unban action scripts

Create your working override file now:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Alternatively, start with a minimal jail.local that only contains what you want to change — cleaner and less likely to conflict with package updates.

Configuring Global Defaults

Open /etc/fail2ban/jail.local and locate or add the [DEFAULT] section. The key tunables:

sudo nano /etc/fail2ban/jail.local
[DEFAULT]
# Hosts to never ban — your own IPs, monitoring servers
ignoreip = 127.0.0.1/8 ::1 203.0.113.50

# How long a ban lasts (seconds, or use time suffixes: 10m, 1h, 1d)
bantime  = 1h

# Window in which findtime failures trigger a ban
findtime  = 10m

# Number of failures before a ban
maxretry = 5

# Backend: systemd is preferred on modern distros; auto falls back correctly
backend = systemd

bantime accepts a multiplier suffix in fail2ban 0.11+: set it to -1 for a permanent ban (use with caution). The bantime.increment feature can also escalate repeat offenders automatically — see jail.conf for details.

The SSH Jail

The bundled sshd jail covers OpenSSH. With backend = systemd, it reads directly from the journal rather than a log file — no path configuration needed.

[sshd]
enabled  = true
port     = ssh
filter   = sshd
backend  = systemd
maxretry = 3
bantime  = 2h
findtime = 5m

If you run SSH on a non-standard port, change port to match:

port = 2222

If your distro still uses flat log files (/var/log/auth.log on older Debian, /var/log/secure on RHEL), set backend and logpath explicitly:

[sshd]
enabled  = true
port     = ssh
filter   = sshd
backend  = auto
logpath  = /var/log/auth.log
maxretry = 3

Choosing a Firewall Action

fail2ban needs to know which firewall to use. The default action varies by distro. Check what's active on your system and set accordingly in [DEFAULT]:

  • nftables (recommended on modern systems): banaction = nftables-multiport
  • firewalld (Fedora/RHEL default): banaction = firewallcmd-rich-rules
  • ufw (Ubuntu): banaction = ufw
  • iptables (legacy fallback): banaction = iptables-multiport
[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports

Reload after any change:

sudo systemctl reload fail2ban

Writing a Custom Filter

Suppose you're running a web app that logs failed logins like this in /var/log/myapp/auth.log:

2024-06-01 14:22:03 LOGIN FAILED user=admin src=198.51.100.77
2024-06-01 14:22:07 LOGIN FAILED user=root src=198.51.100.77

Create a new filter file:

sudo nano /etc/fail2ban/filter.d/myapp-auth.conf
[Definition]
failregex = ^%(__prefix_line)s LOGIN FAILED user=\S+ src=$
ignoreregex =

<HOST> is fail2ban's placeholder for an IPv4 or IPv6 address. %(__prefix_line)s matches the timestamp/hostname prefix automatically. Test your filter against real log data before enabling it:

sudo fail2ban-regex /var/log/myapp/auth.log /etc/fail2ban/filter.d/myapp-auth.conf

The output will report how many lines matched. A realistic result looks like:

Lines: 120 lines, 0 ignored, 14 matched, 106 missed

Once you're satisfied, add the jail in jail.local:

[myapp-auth]
enabled  = true
port     = http,https
filter   = myapp-auth
logpath  = /var/log/myapp/auth.log
maxretry = 5
bantime  = 30m
sudo systemctl reload fail2ban

Verifying Your Setup

Check which jails are active:

sudo fail2ban-client status

Inspect a specific jail:

sudo fail2ban-client status sshd

Sample output (yours will differ):

Status for the jail: sshd
|- Filter
|  |- Currently failed: 2
|  |- Total failed:     47
|  `- Journal matches:  _SYSTEMD_UNIT=sshd.service + _COMM=sshd
`- Actions
   |- Currently banned: 1
   |- Total banned:     8
   `- Banned IP list:   198.51.100.77

To manually unban an IP:

sudo fail2ban-client set sshd unbanip 198.51.100.77

To manually ban one for testing:

sudo fail2ban-client set sshd banip 203.0.113.1

Troubleshooting

Jail shows as inactive after reload

Check for syntax errors in your config:

sudo fail2ban-client -t

IPs are matched but never banned

Confirm the action is working by checking the service log:

sudo journalctl -u fail2ban -n 50 --no-pager

A mismatch between the configured banaction and the active firewall (e.g., specifying nftables-multiport when only iptables is available) will silently fail to ban. Run sudo nft list ruleset or sudo iptables -L to confirm which is in use.

You locked yourself out

If you're on a VPS and lose SSH access, use the provider's console to unban yourself:

sudo fail2ban-client set sshd unbanip YOUR_IP

Then add your static IP to ignoreip in [DEFAULT] and reload. This is exactly why ignoreip should be set before you go live.

fail2ban-regex shows zero matches

The regex anchoring or timestamp format may be off. Use --print-all-matched to debug which lines are being evaluated, and compare carefully against a real log line. Remove %(__prefix_line)s temporarily to isolate the issue.

tested on:Ubuntu 24.04Debian 12Fedora 40Arch rolling

Frequently asked questions

Will fail2ban interfere with legitimate users who mistype their password?
Only if they exceed maxretry within the findtime window. Setting maxretry to 5 and findtime to 10 minutes is generous enough for normal use. Always add your own IPs to ignoreip.
Does fail2ban protect against distributed brute-force attacks from many IPs?
Not effectively. fail2ban bans individual IPs; a botnet spreading attempts across thousands of addresses will stay under the threshold. Use it alongside strong passwords or — better — key-only SSH authentication.
What's the difference between bantime = 1h and bantime.increment = true?
A fixed bantime releases the IP after one hour every time. bantime.increment multiplies the ban duration for repeat offenders — a recidivist gets banned for progressively longer periods, eventually permanently.
Can I use fail2ban with nftables on systems that still have iptables installed?
Yes, but you must explicitly set banaction = nftables-multiport in jail.local; the auto-detection may prefer iptables if it's present. Verify with nft list ruleset after a test ban.
How do I see the full history of bans, not just currently active ones?
fail2ban-client status <jail> shows total banned counts. For full history including timestamps, query the journal: sudo journalctl -u fail2ban | grep 'Ban'.

Related guides