# fail2ban and UFW: use both, and how to connect them

> Use fail2ban with UFW: set banaction = ufw or keep nftables, why ufw limit alone isn't enough, Ubuntu 24.04 setup and how to check bans in ufw status.
>
> Source: https://fail2ban.dev/guides/ufw/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

**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`).

```ini
[DEFAULT]
banaction          = ufw
banaction_allports = ufw

[sshd]
enabled = true
```

## 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

```bash
# or by application profile
sudo ufw limit OpenSSH
```

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 (`MaxAuthTries` defaults 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 `maxretry` failures within `findtime`.
- **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 `limit` SSH if you see connection floods. Skip `limit` if 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](/guides/ssh/) 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](/config/banaction/).

### What the ufw action does

From the upstream `action.d/ufw.conf` (see the [action reference](/actions/)), each ban runs:

```bash
```

- **It blocks all ports.** The rule is `to any` with no port, so the jail's `port` setting is ignored. A client banned by the `sshd` jail 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's `application` option to a UFW application profile (`ufw app list`).
- **`prepend` puts the ban first.** UFW evaluates rules in order, and a ban added after `allow 22/tcp` would never be reached. `prepend` (the default `add` option) inserts it at the top, for IPv4 and IPv6 alike.
- **`reject`, not `deny`.** The client gets an immediate refusal. Use `ufw[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:

```ini
[DEFAULT]
banaction          = ufw[kill-mode=ss]
banaction_allports = ufw[kill-mode=ss]
```

### 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](/config/bantime-increment/) with long bans.

You still see the bans with `sudo fail2ban-client banned` and `sudo nft list table inet f2b-table`. The [nftables guide](/guides/nftables/) 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`.


1. **Enable UFW without locking yourself out.** `sudo ufw allow OpenSSH` (or your SSH port), then `sudo ufw enable`. Check with `sudo ufw status verbose`.
2. **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](/install/ubuntu/)).
3. **Set the ban action.** Add the `[DEFAULT]` block from the top of this page to `/etc/fail2ban/jail.local`, with your own network in `ignoreip`.
4. **Restart.** `sudo fail2ban-client -t && sudo systemctl restart fail2ban`.
5. **Test with a documentation address.** `sudo fail2ban-client set sshd banip 192.0.2.15` prints `1`, and `sudo ufw status | grep 192.0.2.15` shows the reject rule. Remove it with `sudo 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

```bash
```

```bash
```

```bash
```

## 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](/guides/docker/).

## 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:

```nft
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
    }
}
```

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.
