Guides
fail2ban and UFW: use both, and how to connect them
ufw limit and fail2ban look at different things: one counts connections, the other reads what happened in them. Most servers want both. This page compares them, then shows how to make fail2ban ban through UFW or run alongside it.
Use both. ufw limit denies an IP address that opens 6 or more connections to a port within 30 seconds; fail2ban bans an IP after it fails to log in a set number of times, for as long as you choose. To make fail2ban ban through UFW, set banaction = ufw and banaction_allports = ufw under [DEFAULT] in /etc/fail2ban/jail.local and restart fail2ban. UFW must be enabled (sudo ufw enable).
[DEFAULT]
banaction = ufw
banaction_allports = ufw
[sshd]
enabled = trueEach ban then becomes a ufw prepend reject from <IP> to any rule that you can see with sudo ufw status. If UFW is inactive, the rules exist but block nothing.
fail2ban vs ufw limit#
| Feature | fail2ban | ufw limit |
|---|---|---|
| What it counts | Failures found in logs (per service filter) | New connections per source IP to one port |
| Knows if login failed? | Yes | No |
| Threshold | Configurable: maxretry in findtime |
Fixed: 6 in 30 seconds |
| Block duration | Configurable bantime, incremental, permanent |
Only while over the limit |
| Catches slow brute force (1 try/min) | Yes | No |
| Protects | Any service with a log, with per-app jails | Any TCP/UDP port, by connection rate only |
| Can block legit users | Only after real failures | Yes, if they open many connections quickly (scp loops, Ansible, git) |
| Resource use | Python daemon reading logs | None beyond the kernel |
| Setup | jail.local |
sudo ufw limit 22/tcp |
How ufw limit works#
sudo ufw limit 22/tcp
# or by application profile
sudo ufw limit OpenSSHThe rule allows connections normally, but the ufw man page states that it denies connections if an IP address attempts to initiate 6 or more connections within 30 seconds. Under the hood UFW uses the iptables recent match to remember recent sources. The numbers are not exposed as options: changing them means editing UFW's rule files by hand.
It's free, instant, and blunts high-speed scanners and connection floods. What it misses:
- Many guesses per connection. OpenSSH accepts several authentication attempts per connection (
MaxAuthTriesdefaults to 6), so a bot can try dozens of passwords while opening only a few connections. - Slow attackers. A bot that tries one password every 10 seconds never trips the limit, all day long — but fail2ban bans it after
maxretryfailures withinfindtime. - Application attacks. A WordPress or Nextcloud login brute force over keep-alive HTTPS connections looks like normal traffic.
- No memory. As soon as the source slows down, it is allowed again. There is no "ban for a day".
Do I need fail2ban if I use UFW?#
Yes, if you expose any service that accepts logins. UFW is a firewall front-end: it decides which ports are open. It doesn't read logs, so on its own it does nothing about someone guessing passwords on a port you've opened. Split the jobs:
- UFW for the policy: deny incoming by default, allow only the ports you serve, optionally
limitSSH if you see connection floods. Skiplimitif you or your automation open many SSH connections in bursts. - fail2ban for the bans: watch logs and block attackers on the ports you left open. Legitimate users who connect a lot but log in successfully are never touched.
Better than both for SSH: key-only authentication (PasswordAuthentication no). With no passwords to guess, both tools become noise reduction rather than your main defense. The SSH guide covers the sshd jail in depth.
Use fail2ban with UFW#
There are two ways to run them together:
| Setup | Where bans live | Visible in ufw status |
Good for |
|---|---|---|---|
banaction = ufw |
UFW user rules | Yes, with a comment | One place to see and manage every block |
| Default action (nftables on Ubuntu 24.04+) next to UFW | fail2ban's own inet f2b-table |
No | Many bans, speed, per-port bans |
Both block the attacker. UFW on Ubuntu manages iptables rules, which on Ubuntu 22.04 and newer go through the nf_tables kernel backend. fail2ban's nftables action creates a separate table whose input chain runs at priority -1, just before UFW's rules. A packet rejected by either one is gone. Neither tool deletes the other's rules, so the "don't mix firewalls" warning doesn't apply here. More on the choice in ban actions.
What the ufw action does#
From the upstream action.d/ufw.conf (see the action reference), each ban runs:
ufw prepend reject from <ip> to any comment "by Fail2Ban after <failures> attempts against <jail>"and each unban deletes the same rule. Things to know:
- It blocks all ports. The rule is
to anywith no port, so the jail'sportsetting is ignored. A client banned by thesshdjail can't reach your web server either. That's usually fine, but it differs from the per-port nftables and iptables actions. To limit a ban to one service, set the action'sapplicationoption to a UFW application profile (ufw app list). prependputs the ban first. UFW evaluates rules in order, and a ban added afterallow 22/tcpwould never be reached.prepend(the defaultaddoption) inserts it at the top, for IPv4 and IPv6 alike.reject, notdeny. The client gets an immediate refusal. Useufw[blocktype=deny]to drop packets silently.- There's no start or stop step. Bans are plain UFW rules, so they survive a UFW reload. When fail2ban stops, it removes them one by one.
Kill connections that are already open#
UFW accepts packets of established connections early in /etc/ufw/before.rules, before any user rule. A banned SSH session that's already logged in, or a keep-alive HTTP connection, keeps working until it closes. The action can kill those sessions after the ban:
[DEFAULT]
banaction = ufw[kill-mode=ss]
banaction_allports = ufw[kill-mode=ss]kill-mode=ss runs ss -K dst "[<ip>]", which closes the banned address's TCP sockets. kill-mode=conntrack runs conntrack -D -s <ip> instead (install the conntrack package), which also covers UDP flows tracked by the kernel.
When to keep nftables instead of banaction = ufw#
The ufw action calls the ufw command once per ban and once per unban. Each call updates UFW's rule files and its firewall chains. With a handful of bans that's fine. With thousands (a busy recidive jail, a web server under a botnet), the UFW rule list gets long and each change gets slower. nftables adds an element to a set instead, which stays fast with any number of bans.
Keep the default nftables action when:
- you expect hundreds or thousands of simultaneous bans,
- you want per-port bans (ban web abusers from 80/443 only),
- you use incremental ban times with long bans.
You still see the bans with sudo fail2ban-client banned and sudo nft list table inet f2b-table. The nftables guide covers it.
fail2ban and UFW on Ubuntu 24.04#
On Ubuntu 24.04 the package's /etc/fail2ban/jail.d/defaults-debian.conf sets banaction = nftables and backend = systemd. A banaction in jail.local overrides it, because jail.local is read after jail.d/*.conf.
- Enable UFW without locking yourself out.
sudo ufw allow OpenSSH(or your SSH port), thensudo ufw enable. Check withsudo ufw status verbose. - Install fail2ban.
sudo apt install fail2ban. On Ubuntu 24.04 that's version 1.0.2 with the sshd jail enabled by default (Ubuntu install guide). - Set the ban action. Add the
[DEFAULT]block from the top of this page to/etc/fail2ban/jail.local, with your own network inignoreip. - Restart.
sudo fail2ban-client -t && sudo systemctl restart fail2ban. - Test with a documentation address.
sudo fail2ban-client set sshd banip 192.0.2.15prints1, andsudo ufw status | grep 192.0.2.15shows the reject rule. Remove it withsudo fail2ban-client set sshd unbanip 192.0.2.15.
Any jail that reads a log file (web servers, apps) needs backend = auto, because defaults-debian.conf sets backend = systemd for all jails.
Check bans in UFW#
sudo ufw status numberedfail2ban's rules are at the top with the comment by Fail2Ban after N attempts against <jail>. To list only those:
sudo ufw status | grep -i fail2banCompare with fail2ban's own view, which is the authoritative one:
sudo fail2ban-client bannedIf an address shows in UFW but not in fail2ban, the rule is stale, typically left behind after a crash or a change of banaction. Delete it by number with sudo ufw delete <N>. Unban through fail2ban, not UFW: sudo ufw delete alone leaves the ban in fail2ban's records and database, so fail2ban still counts the address as banned. See unban an IP.
Docker and UFW#
Docker publishes container ports by writing its own iptables rules that are evaluated before UFW's. Neither a UFW deny rule nor a fail2ban ban through UFW blocks a published container port. To ban traffic to containers, use an iptables action on the DOCKER-USER chain. See the Docker guide.
Rate limiting without UFW#
ufw limit is a wrapper around a kernel rate limit, which you can also write directly. fail2ban and iptables/nftables aren't competitors: they are the kernel's packet filters, and fail2ban is one of the programs that writes ban rules into them. "fail2ban vs iptables" usually means log-based banning vs a firewall rate limit.
This nftables ruleset drops new SSH connections from any address that exceeds 6 per minute (with a small burst), tracking IPv4 and IPv6 separately:
table inet ratelimit {
set ssh4 {
type ipv4_addr
flags dynamic,timeout
timeout 2m
}
set ssh6 {
type ipv6_addr
flags dynamic,timeout
timeout 2m
}
chain input {
type filter hook input priority filter; policy accept;
tcp dport 22 ct state new update @ssh4 { ip saddr limit rate over 6/minute burst 3 packets } drop
tcp dport 22 ct state new update @ssh6 { ip6 saddr limit rate over 6/minute burst 3 packets } drop
}
}Load it with sudo nft -f /etc/nftables.d/ssh-ratelimit.nft and include it from /etc/nftables.conf to make it permanent. Older documentation calls this construct a meter. With iptables, the recent module (what ufw limit uses) and hashlimit do the same job; the iptables guide shows how fail2ban's iptables actions fit in alongside them.
For HTTP, rate-limit in the web server instead: Nginx's limit_req rejects excess requests, and fail2ban's nginx-limit-req jail can then ban clients that keep hitting the limit.
FAQ#
Do I need fail2ban if I have UFW?#
Yes, if you expose any service that accepts logins. UFW only decides which ports are open; it doesn't read logs, so it can't see failed passwords. ufw limit helps against floods but misses slow and multi-attempt brute force. fail2ban reads the logs and bans attackers, and it can use UFW itself as the ban action.
Should I use ufw limit or fail2ban?#
Use both. ufw limit blocks an IP that opens 6 or more connections in 30 seconds, which slows floods, but it can't tell failed logins from good ones and misses slow attackers. fail2ban reads the logs and bans only clients that actually fail authentication, for as long as you choose.
What does ufw limit do exactly?#
It allows connections to the port but denies a source IP that attempts 6 or more connections within 30 seconds, using the iptables recent match. The threshold is fixed and isn't configurable from the ufw command. The block lifts as soon as the source falls back under the limit.
How do I set the fail2ban banaction to ufw?#
Add banaction = ufw and banaction_allports = ufw under [DEFAULT] in /etc/fail2ban/jail.local, then restart fail2ban. Bans become reject rules at the top of UFW's list, visible in sudo ufw status. On Ubuntu 24.04 this overrides the packaged banaction = nftables. Make sure UFW itself is active with sudo ufw enable.
Does the fail2ban ufw action block only the jail's port?#
No. The ufw action adds reject from <IP> to any, which blocks every port, regardless of the jail's port setting. If you need per-port bans, use the nftables or iptables actions, or set the action's application option to a UFW application profile.
Why can a banned IP still reach my server through UFW?#
Three common reasons: UFW is inactive (ufw status says inactive), the connection was already established (UFW accepts established traffic before user rules, so use kill-mode=ss), or the service is a Docker published port, which bypasses UFW entirely.
What is the difference between fail2ban and iptables?#
iptables (and its successor nftables) is the Linux packet filter that actually drops traffic. fail2ban is a log monitor that decides which addresses to block and then adds rules to iptables or nftables through its ban actions. You use them together; fail2ban without a firewall backend can't block anything.
Spotted something wrong or out of date? Tell us — every page is checked against the current fail2ban release.