# fail2ban for Vaultwarden

> Protect Vaultwarden with fail2ban: enable LOG_FILE, use the vaultwarden filter from fail2ban 1.1.1, ban in DOCKER-USER, add a stricter admin page jail, and test.
>
> Source: https://fail2ban.dev/guides/vaultwarden/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

To protect Vaultwarden with fail2ban, set `LOG_FILE=/data/vaultwarden.log` on the container so failed logins are written to a file in the data volume, then enable the stock `vaultwarden` jail with `logpath` pointing to that file on the host, `backend = auto`, and — because Vaultwarden runs in Docker — `banaction = iptables-allports` and `chain = DOCKER-USER`. The `vaultwarden` filter is included in fail2ban 1.1.1 and later.

```ini
[vaultwarden]
enabled   = true
backend   = auto
logpath   = /opt/vaultwarden/vw-data/vaultwarden.log
banaction = iptables-allports
chain     = DOCKER-USER
maxretry  = 5
findtime  = 1h
bantime   = 4h
```
sudo fail2ban-client reload
sudo fail2ban-client status vaultwarden
```

Vaultwarden logs to stdout by default. Set `LOG_FILE` to a path inside the `/data` volume so the file appears on the host:

```yaml
services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    restart: unless-stopped
    environment:
      DOMAIN: "https://vault.example.com"
      LOG_FILE: "/data/vaultwarden.log"
      TZ: "Europe/Berlin"
    volumes:
      - ./vw-data:/data
    ports:
      - "127.0.0.1:8080:80"
```
cd /opt/vaultwarden && docker compose up -d
ls -l /opt/vaultwarden/vw-data/vaultwarden.log
```

Rotate the file with a logrotate rule on the host (`copytruncate` avoids restarting the container).

## Step 2: the vaultwarden filter

fail2ban 1.1.1 includes [`filter.d/vaultwarden.conf`](/guides/vaultwarden/#filter-vaultwarden) and a disabled `[vaultwarden]` jail (`port = http,https`, `logpath = /var/log/vaultwarden.log`). The filter matches three events:

```log
[2026-10-25 18:36:11.456][vaultwarden::api::admin][ERROR] Invalid admin token. IP: 203.0.113.42
[2026-10-25 18:36:12.789][vaultwarden::api::core::two_factor::authenticator][ERROR] Invalid TOTP code! Server time: 2026-10-25 18:36:12 UTC IP: 203.0.113.42
```

### Older fail2ban without the filter

The filter is new in 1.1.1. Packages based on 1.1.0 or older (Debian 13, Ubuntu 24.04 and 26.04, EPEL) don't have it — `ls /etc/fail2ban/filter.d/vaultwarden.conf` tells you. Create it with the upstream definition:

```ini
[Definition]
failregex = ^\s*(?:\[\]\s*)?\[vaultwarden::api::(?:identity|admin|core::two_factor::authenticator)?\]\[ERROR\] (?:Invalid admin token|Invalid TOTP code|Username or password is incorrect)[\.!](?:\s+(?!IP:)\S+)* IP: <ADDR>(?:\. Username: <F-USER>\S+</F-USER>)?
ignoreregex =
```

## Step 3: ban where Docker traffic goes

Vaultwarden's published port is DNATed to the container, so packets cross the `FORWARD` chain, not `INPUT`. A default fail2ban ban is recorded but doesn't block the container. `chain = DOCKER-USER` with `iptables-allports` fixes it; allports avoids the trap that, in `DOCKER-USER`, ports are already translated to the container port (80, not 8080). Make fail2ban start after Docker so the chain exists (`sudo systemctl edit fail2ban` → `[Unit]` `After=docker.service`). Details and the nftables variant: [fail2ban with Docker containers](/guides/docker/).

**Behind a reverse proxy** (the usual setup, since Vaultwarden needs HTTPS):

- Vaultwarden reads the client address from the `X-Real-IP` header by default (`IP_HEADER`). Make sure your proxy sets it, e.g. `proxy_set_header X-Real-IP $remote_addr;` in nginx; otherwise the log shows the proxy's IP. Recent versions also have `IP_HEADER_TRUSTED_PROXIES` to restrict which addresses may set the header.
- The ban must hit the proxy's traffic. If the proxy runs in Docker on the same host (Nginx Proxy Manager, Traefik, Caddy), `DOCKER-USER` + allports covers it. If Vaultwarden is bound to `127.0.0.1` as above and the proxy runs on the host, use the default action instead — the proxy's ports are on `INPUT`.
- Behind Cloudflare, ban at Cloudflare's edge: see the [Cloudflare guide](/guides/cloudflare/).

## A stricter jail for the admin page

The `/admin` page is protected by `ADMIN_TOKEN`; nobody legitimate should get it wrong more than once or twice. A second jail with its own small filter bans admin-token guessing faster and longer:

```ini
[Definition]
failregex = ^\s*(?:\[\]\s*)?\[vaultwarden::api::admin\]\[ERROR\] Invalid admin token[\.!] IP: <ADDR>
```
[vaultwarden-admin]
enabled   = true
backend   = auto
filter    = vaultwarden-admin
logpath   = /opt/vaultwarden/vw-data/vaultwarden.log
banaction = iptables-allports
chain     = DOCKER-USER
maxretry  = 3
findtime  = 1d
bantime   = 1w
```

## Test it

```bash
sudo fail2ban-client status vaultwarden
sudo iptables -L DOCKER-USER -n --line-numbers
```

## Official Bitwarden server

For the official Bitwarden self-hosted server (not Vaultwarden), fail2ban ships a separate [`bitwarden` filter](/guides/vaultwarden/#filter-bitwarden) and jail, which reads `Failed login attempt` lines from `/home/*/bwdata/logs/identity/Identity/log.txt`:

```ini
[bitwarden]
enabled   = true
backend   = auto
banaction = iptables-allports
chain     = DOCKER-USER
```

{{component:filter-ref:vaultwarden.bitwarden}}

## FAQ

### Does fail2ban have a Vaultwarden filter?

Yes, since fail2ban 1.1.1: `filter.d/vaultwarden.conf` with a matching `[vaultwarden]` jail. It catches wrong passwords, wrong TOTP codes and wrong admin tokens. Older packages lack it; create the file yourself with the upstream `failregex` shown on this page.

### How do I make Vaultwarden log failed logins to a file?

Set the environment variable `LOG_FILE=/data/vaultwarden.log` on the container and recreate it. The file then appears in the host directory mounted at `/data`. Keep `EXTENDED_LOGGING` enabled (the default) and the log level at `info`, and set `TZ` to the host's timezone.

### Why does fail2ban ban the IP but Vaultwarden is still reachable?

Because Docker forwards the published port to the container through the `FORWARD` chain, and the default ban sits in `INPUT`. Set `banaction = iptables-allports` and `chain = DOCKER-USER` in the jail, reload fail2ban, and check `iptables -L DOCKER-USER -n` for the `f2b-vaultwarden` jump.

### Why does the Vaultwarden log show my proxy's IP?

Vaultwarden takes the client address from the header named in `IP_HEADER`, by default `X-Real-IP`. If your reverse proxy doesn't set that header, Vaultwarden logs the proxy. Add `proxy_set_header X-Real-IP $remote_addr;` (nginx) or the equivalent in your proxy.

### Does the vaultwarden filter also protect the admin page?

Yes. The stock filter matches `Invalid admin token`, so admin-page guesses count towards the same jail. For faster, longer bans on the admin page, add a second jail with a filter that matches only `[vaultwarden::api::admin]` lines and a low `maxretry`.
