ESC

Start typing to search across invoices, services, domains, tickets, and more...

Search... Ctrl+K
Linux Server

How to Install and Configure Fail2ban to Protect SSH and Nginx Websites (Debian, Ubuntu, Rocky)

6 steps 15 min read 5 views 0
On this page

As soon as a server has a public IP, bots start guessing SSH passwords within minutes, and websites get scanned for admin pages and hammered with requests. Fail2ban watches your logs and, when an IP fails too often or requests too fast, bans it through the firewall automatically. This guide covers installing Fail2ban on Debian/Ubuntu and Rocky Linux/AlmaLinux, writing jail.local, enabling the sshd jail against SSH brute force, protecting websites with the nginx-limit-req and nginx-http-auth jails, ufw/firewalld integration, unbanning and whitelisting.

Before you start, add your own public IP to the whitelist (ignoreip in Step 2) and keep one SSH session open so you cannot lock yourself out while testing.

Step 1: Install Fail2ban

On Debian/Ubuntu install from the standard repositories together with python3-systemd so Fail2ban can read SSH logins from the systemd journal (Debian 12 and later no longer create /var/log/auth.log by default). On Rocky/AlmaLinux Fail2ban comes from EPEL, and the fail2ban-firewalld package makes it ban through firewalld.

# Debian / Ubuntu
apt update
apt install -y fail2ban python3-systemd

# Rocky Linux / AlmaLinux (EPEL)
dnf install -y epel-release
dnf install -y fail2ban fail2ban-firewalld

systemctl enable --now fail2ban
fail2ban-client version

Step 2: Create jail.local and Enable the sshd Jail

Never edit jail.conf, which is replaced on upgrades; keep all changes in jail.local. The configuration below bans an IP for one hour after three failed logins within ten minutes, and repeat offenders get progressively longer bans of up to a week. If you moved SSH to another port (see our change SSH port tutorial), set port to that number, otherwise the ban will not cover the real port.

# Never edit jail.conf (it is overwritten on upgrade); use jail.local
cat > /etc/fail2ban/jail.local <<'EOF'
[DEFAULT]
# Your own IPs are never banned (office/home IP, monitoring, other servers)
ignoreip = 127.0.0.1/8 ::1 198.51.100.20 203.0.113.0/24
bantime  = 1h
findtime = 10m
maxretry = 5
# Repeat offenders get longer bans, up to one week
bantime.increment = true
bantime.maxtime   = 1w

[sshd]
enabled  = true
# use your custom port if you changed it, e.g. port = 2222
port     = ssh
backend  = systemd
maxretry = 3
EOF

fail2ban-client -t            # test the configuration
systemctl restart fail2ban
fail2ban-client status
fail2ban-client status sshd

Step 3: Set banaction for ufw or firewalld

banaction decides how Fail2ban blocks an address. Use ufw on Ubuntu/Debian with ufw; on Rocky/AlmaLinux the fail2ban-firewalld package already selects firewalld rich rules; without either firewall use nftables-multiport. Choose exactly one, put it in the [DEFAULT] section of jail.local and restart Fail2ban (see our Linux firewall tutorial for firewall basics).

# Debian / Ubuntu with ufw - add to [DEFAULT] in /etc/fail2ban/jail.local
banaction = ufw

# Rocky / AlmaLinux with firewalld - set automatically by fail2ban-firewalld
# (/etc/fail2ban/jail.d/00-firewalld.conf), or explicitly:
banaction = firewallcmd-rich-rules

# Plain nftables without ufw/firewalld
banaction = nftables-multiport

# Check that bans really reach the firewall
ufw status numbered | head
firewall-cmd --list-rich-rules
nft list ruleset | grep -A5 f2b

Step 4: Enable Rate Limiting and Auth Logging in Nginx

The nginx-limit-req jail relies on Nginx's limit_req module: when a client exceeds the rate, Nginx writes "limiting requests" to error.log and Fail2ban bans the client. nginx-http-auth watches failed Basic Auth logins, which suits admin directories. Tune the rate to your traffic so real visitors are not caught.

# /etc/nginx/nginx.conf, inside http { }
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;

# inside the server { } or location { } you want to protect
limit_req zone=perip burst=20 nodelay;

# protected area with basic auth (optional)
location /admin/ {
    auth_basic           "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

nginx -t && systemctl reload nginx

Step 5: Enable the nginx-limit-req and nginx-http-auth Jails

A separate file in jail.d keeps the Nginx jails tidy. Before relying on them, run fail2ban-regex to confirm that the filter actually matches lines in your log. Control panels such as aaPanel store Nginx logs elsewhere, so adjust logpath to the real location.

cat > /etc/fail2ban/jail.d/nginx.local <<'EOF'
[nginx-http-auth]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log

[nginx-limit-req]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
findtime = 1m
maxretry = 10
bantime  = 2h
EOF

# Check that the filter matches lines in your log
fail2ban-regex /var/log/nginx/error.log /etc/fail2ban/filter.d/nginx-limit-req.conf

fail2ban-client reload
fail2ban-client status nginx-limit-req
If your site is behind a CDN, Nginx logs the CDN node addresses, and banning them would block many legitimate visitors. Restore the real visitor IP with real_ip first (see our Nginx reverse proxy tutorial), or rate limit at the CDN instead.

Step 6: Unban an IP and Whitelist Your Address

You can unban from one jail or from all of them. addignoreip lasts only until Fail2ban restarts; for a permanent whitelist add the IP to ignoreip in jail.local and reload. If you banned yourself and cannot SSH in, connect from another network (for example a phone hotspot), use the console in the client area if available, or open a ticket.

fail2ban-client status sshd                      # list banned IPs
fail2ban-client set sshd unbanip 198.51.100.20   # unban from one jail
fail2ban-client unban 198.51.100.20              # unban from all jails
fail2ban-client unban --all                      # clear every ban

fail2ban-client set sshd addignoreip 198.51.100.20   # whitelist until restart
# Permanent whitelist: add the IP to "ignoreip" in jail.local, then
fail2ban-client reload

tail -f /var/log/fail2ban.log                    # watch bans live

FAQ

Fail2ban fails to start and complains about a missing log file?

This is typical on Debian 12 and later, which no longer write auth.log. Set backend = systemd in the sshd section and install python3-systemd. fail2ban-client -t shows the exact error.

The status shows bans, but the IP can still connect?

Usually banaction does not match the firewall in use, or port differs from the real SSH port. Use the commands in Step 3 to check that matching rules exist.

Can Fail2ban replace SSH key login?

No. Fail2ban only slows brute force down. Disabling password login and using SSH keys is the real fix; use both together for the best protection.

Still stuck after following these steps? Open a support ticket and the IMIDC 24/7 technical team will help. Include the server IP, OS version, the commands you ran and the full error output so we can pinpoint the issue faster.

Was this answer helpful?

Related Tutorials