# fail2ban for Nextcloud

> Protect Nextcloud with fail2ban: a tested Login failed filter for nextcloud.log, the jail, Docker, AIO and snap log paths, and trusted_proxies.
>
> Source: https://fail2ban.dev/guides/nextcloud/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

To protect Nextcloud with fail2ban, create a filter that matches `"remoteAddr":"<ADDR>"` and `"message":"Login failed:` in Nextcloud's JSON log, then add a jail with `logpath` set to your `nextcloud.log` (by default `<datadirectory>/nextcloud.log`), `port = http,https` and `backend = auto`. Nextcloud must log at `loglevel` 2 or lower, because failed logins are written as warnings. For Docker and AIO, add `banaction = iptables-allports` and `chain = DOCKER-USER`.

```ini
[Definition]
failregex = ^\{.*?"remoteAddr":"<ADDR>".*?"message":"(?:Login failed|Two-factor challenge failed):
datepattern = "time":"%%Y-%%m-%%dT%%H:%%M:%%S%%z"
```
[nextcloud]
enabled  = true
backend  = auto
filter   = nextcloud
logpath  = /var/www/nextcloud/data/nextcloud.log
port     = http,https
maxretry = 5
findtime = 1h
bantime  = 1d
```
sudo fail2ban-client reload
sudo fail2ban-client status nextcloud
```

Nextcloud has built-in brute-force protection (`'auth.bruteforce.protection.enabled' => true` by default). It records failed attempts per IP and **delays** further requests from that address, so guessing gets very slow. It doesn't block the address, and the delayed requests still reach PHP. You can inspect and reset it with `occ`:

```bash
sudo -u www-data php occ security:bruteforce:reset 203.0.113.42
```

## What Nextcloud logs on a failed login

Nextcloud writes one JSON object per line. A failed web login is logged at level 2 (warning):

```json
```

```bash
sudo -u www-data php occ config:system:set loglevel --value=2 --type=integer
```

- `"remoteAddr":"<ADDR>"` is the address Nextcloud determined for the request — the real client when `trusted_proxies` is set correctly (see below).
- `.*?` skips the other fields. Values controlled by the client, such as the URL or user name, are JSON-escaped, so they can't contain an unescaped `"message":"` and fake a match.
- The `time` field uses ISO 8601 with a UTC offset (`2026-10-25T18:36:10+00:00`), and `%%z` reads that offset. fail2ban therefore gets the right time whatever Nextcloud's `logtimezone` is. If you changed `logdateformat` to something without an offset, adjust `datepattern` and set the jail's `logtimezone` to Nextcloud's timezone.

We tested the filter with `fail2ban-regex` against a UTC line, an IPv6 line with a `+02:00` offset and an unrelated entry: both failed logins matched with the correct time, the third line was missed. Nextcloud's own documentation has an equivalent filter that additionally catches `Trusted domain error.` messages.

## Find your nextcloud.log

The log lives in the data directory unless `logfile` is set in `config.php`:

```bash
sudo -u www-data php occ config:system:get datadirectory
```
|---|---|
| Manual / package install | `<datadirectory>/nextcloud.log`, e.g. `/var/www/nextcloud/data/nextcloud.log` |
| Snap | `/var/snap/nextcloud/current/logs/nextcloud.log` |
| Docker, official `nextcloud` image | `<volume for /var/www/html>/data/nextcloud.log`, e.g. `/var/lib/docker/volumes/nextcloud_nextcloud/_data/data/nextcloud.log` |
| Nextcloud AIO | `/var/lib/docker/volumes/nextcloud_aio_nextcloud/_data/data/nextcloud.log` |

For Docker named volumes, `docker volume inspect <name>` prints the `Mountpoint`. The AIO path is the one given in AIO's own README. The log must be readable by root (fail2ban) — it is by default.

## Nextcloud in Docker and AIO

Docker forwards published ports to the container, so bans in the `INPUT` chain don't apply. Use `DOCKER-USER`:

```ini
[nextcloud]
enabled   = true
backend   = auto
filter    = nextcloud
logpath   = /var/lib/docker/volumes/nextcloud_aio_nextcloud/_data/data/nextcloud.log
banaction = iptables-allports
chain     = DOCKER-USER
maxretry  = 5
findtime  = 1h
bantime   = 1d
```

## Nextcloud behind a reverse proxy

Behind nginx, Apache, Caddy, Traefik, NPM or Cloudflare, Nextcloud sees the proxy's address and logs it as `remoteAddr` — and fail2ban bans the proxy. Tell Nextcloud which proxies to trust and which header carries the client:

```php
'trusted_proxies' => ['10.0.0.5', '172.18.0.0/16'],
'forwarded_for_headers' => ['HTTP_X_FORWARDED_FOR'],
```

## Test it

```bash
```

```bash
```
2026-10-25 18:36:11,027 fail2ban.actions        [1997]: NOTICE  [nextcloud] Ban 203.0.113.42
```

## FAQ

### What is the fail2ban filter for Nextcloud?

fail2ban ships none, so create `/etc/fail2ban/filter.d/nextcloud.conf` with `failregex = ^\{.*?"remoteAddr":"<ADDR>".*?"message":"(?:Login failed|Two-factor challenge failed):` and `datepattern = "time":"%%Y-%%m-%%dT%%H:%%M:%%S%%z"`. It matches the JSON lines Nextcloud writes for failed logins and failed second factors.

### Where is nextcloud.log?

By default in the data directory: `<datadirectory>/nextcloud.log`, unless `logfile` is set in `config.php`. Snap installs use `/var/snap/nextcloud/current/logs/nextcloud.log`; AIO uses `/var/lib/docker/volumes/nextcloud_aio_nextcloud/_data/data/nextcloud.log` on the host.

### Do I need fail2ban if Nextcloud has brute-force protection?

Nextcloud's protection only slows requests from an address down; it never blocks it, and every request still costs PHP time. fail2ban drops the address at the firewall after a few failures. Running both is common; Nextcloud's own hardening guide describes a fail2ban setup on top of the built-in protection.

### How do I use fail2ban with Nextcloud AIO?

Run fail2ban on the host, point the jail's `logpath` at `/var/lib/docker/volumes/nextcloud_aio_nextcloud/_data/data/nextcloud.log`, and add `banaction = iptables-allports` with `chain = DOCKER-USER`, so bans apply to traffic Docker forwards to the AIO containers. Alternatively, add AIO's community fail2ban container.
