# fail2ban monitoring, dashboards and web UIs

> Monitor fail2ban with the Prometheus exporter, Grafana, node_exporter or Zabbix, and compare GUIs and web UIs like Webmin and fail2ban-ui, with security tips.
>
> Source: https://fail2ban.dev/guides/monitoring/ · Updated 2026-10-25 · Checked against fail2ban 1.1.1
> fail2ban.dev is an independent guide, not the official fail2ban project.

To monitor fail2ban, run **fail2ban-prometheus-exporter** (by Hector Smith, on GitLab as `hctrdev/fail2ban-prometheus-exporter`). It reads `/var/run/fail2ban/fail2ban.sock` and serves metrics such as `f2b_jail_banned_current{jail="sshd"}` on port 9191 for Prometheus and the project's Grafana dashboard. fail2ban has no official GUI; Webmin's module or a third-party web UI such as fail2ban-ui adds one.

## What there is to monitor

Every graph, dashboard and GUI shows the same data `fail2ban-client` gives you:

```text
$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed:	2
|  |- Total failed:	6
|  `- File list:	/var/log/auth.log
`- Actions
   |- Currently banned:	1
   |- Total banned:	1
   `- Banned IP list:	203.0.113.42
```

## Prometheus exporter: fail2ban-prometheus-exporter

A Go exporter that talks to the fail2ban socket. The latest release at the time of writing is v0.10.3 (April 2025). Binaries are on the project's GitLab releases page, and it's packaged in the AUR and nixpkgs.

### Run it as a binary

```bash
```

### Run it in Docker

```yaml
services:
  fail2ban-exporter:
    image: registry.gitlab.com/hctrdev/fail2ban-prometheus-exporter:latest
    volumes:
      - /var/run/fail2ban/:/var/run/fail2ban:ro
    ports:
      - "127.0.0.1:9191:9191"
    restart: unless-stopped
```

### Metrics

All names start with `f2b_`:

| Metric | Meaning |
|---|---|
| `f2b_up` | 1 if the exporter is running |
| `f2b_errors{type="socket_conn"}`, `{type="socket_req"}` | Errors talking to the fail2ban socket |
| `f2b_jail_count` | Number of jails |
| `f2b_jail_banned_current{jail}` | IPs banned right now |
| `f2b_jail_banned_total{jail}` | Bans since fail2ban started (includes expired) |
| `f2b_jail_failed_current{jail}` | Current failures being counted |
| `f2b_jail_failed_total{jail}` | Failures since fail2ban started |
| ban time, find time, max retry per jail | The jail's configuration, in seconds / count |
| `f2b_version` | Exporter and fail2ban versions as labels |

### Prometheus scrape config and alerts

```yaml
scrape_configs:
  - job_name: fail2ban
    static_configs:
      - targets: ["server1.example.com:9191", "server2.example.com:9191"]
```
groups:
  - name: fail2ban
    rules:
      - alert: Fail2banSocketErrors
        expr: increase(f2b_errors{type="socket_conn"}[10m]) > 0
        for: 10m
        annotations:
          summary: "fail2ban not reachable on {{ $labels.instance }}"
      - alert: Fail2banBanSurge
        expr: increase(f2b_jail_banned_total[1h]) > 200
        annotations:
          summary: "{{ $labels.jail }} banned more than 200 IPs in an hour on {{ $labels.instance }}"
      - alert: Fail2banExporterDown
        expr: up{job="fail2ban"} == 0
        for: 5m
```

## Grafana dashboard for fail2ban

The exporter repository includes a sample dashboard at `_examples/grafana/dashboard.json` (Grafana 9.1.8 or newer). In Grafana: **Dashboards → New → Import**, paste the JSON, pick your Prometheus data source. It has an `instance` variable for multiple servers.

Dashboards on grafana.com titled "fail2ban" are built for *different* exporters, and their metric names (such as `fail2ban_banned_current`) won't match this exporter's `f2b_` metrics. Check a panel's query before importing one.

Other exporters exist, for example `fail2ban-exporter` by dyp2000 (Go, `fail2ban_*` metrics) and `fail2ban-geo-exporter`, which exports one series per banned IP with GeoIP labels for map panels. Per-IP series create high cardinality on busy servers.

## node_exporter textfile collector

If node_exporter already runs on the host, skip the extra daemon. A script writes a `.prom` file every minute and node_exporter's textfile collector serves it:

```bash
#!/bin/sh
# Write fail2ban metrics for node_exporter's textfile collector.
set -eu
DIR=${1:-/var/lib/prometheus/node-exporter}
OUT="$DIR/fail2ban.prom"
TMP="$OUT.$$"

metric() { # name type help label-in-status
  echo "# HELP $1 $3"
  echo "# TYPE $1 $2"
  for j in $JAILS; do
    fail2ban-client status "$j" |
      awk -F'\t' -v j="$j" -v m="$1" -v k="$4:" '$0 ~ k {print m "{jail=\"" j "\"} " $2}'
  done
}

{
  echo '# HELP fail2ban_up 1 if fail2ban-client could reach the server.'
  echo '# TYPE fail2ban_up gauge'
  if fail2ban-client ping >/dev/null 2>&1; then
    echo 'fail2ban_up 1'
    JAILS=$(fail2ban-client status | awk -F'\t' '/Jail list:/ {print $2}' | tr ',' ' ')
    metric fail2ban_banned_current gauge   'IPs currently banned per jail.' 'Currently banned'
    metric fail2ban_failed_current gauge   'Current failures per jail.'     'Currently failed'
    metric fail2ban_banned_total   counter 'Bans since the jail started.'   'Total banned'
  else
    echo 'fail2ban_up 0'
  fi
} > "$TMP"
mv "$TMP" "$OUT"
```

```bash
echo '* * * * * root /usr/local/bin/fail2ban-textfile.sh /var/lib/prometheus/node-exporter' | sudo tee /etc/cron.d/fail2ban-textfile
```

## Zabbix

Zabbix's integrations page lists a community fail2ban template with jail discovery (low-level discovery), service status and ban graphs, for Zabbix 5.0 and newer. If you'd rather build it yourself, three agent items are enough. The agent runs as the `zabbix` user, which can't open the fail2ban socket, so give it a narrow sudo rule:

```bash
#!/bin/sh
# f2b-zabbix.sh discover | banned <jail> | failed <jail>
case "$1" in
  discover)
    fail2ban-client status | awk -F'\t' '/Jail list:/ {print $2}' | tr ',' '\n' |
      awk 'NF {printf "%s{\"{#JAIL}\":\"%s\"}", (n++ ? "," : "["), $1} END {print (n ? "]" : "[]")}' ;;
  banned) fail2ban-client status "$2" | awk -F'\t' '/Currently banned:/ {print $2}' ;;
  failed) fail2ban-client status "$2" | awk -F'\t' '/Currently failed:/ {print $2}' ;;
esac
```
zabbix ALL=(root) NOPASSWD: /usr/local/bin/f2b-zabbix.sh
```
UserParameter=fail2ban.discovery,sudo /usr/local/bin/f2b-zabbix.sh discover
UserParameter=fail2ban.banned[*],sudo /usr/local/bin/f2b-zabbix.sh banned "$1"
UserParameter=fail2ban.failed[*],sudo /usr/local/bin/f2b-zabbix.sh failed "$1"
```

## Alerts from the fail2ban log

For a single server, the fail2ban log is often enough. Every ban is one line:

```log
```
- **Email or chat per ban:** fail2ban can send mail on each ban ([email alerts](/config/email-alerts/)), or push to 120+ services with the `apprise` action. For busy jails, a daily summary is more useful than one message per ban.
- **Quick look without tooling:** `grep -c 'Ban ' /var/log/fail2ban.log` or the [log analyzer](/tools/log-analyzer/). Field-by-field explanations are on the [fail2ban log page](/logs/).

## Does fail2ban have a GUI?

No. The upstream project ships only the `fail2ban-server` daemon and the `fail2ban-client` command line. fail2ban runs as root because it changes firewall rules, and it's controlled through a Unix socket (`/var/run/fail2ban/fail2ban.sock`). Anything that can talk to that socket can ban, unban, reconfigure jails and even define action commands, which fail2ban then executes **as root**. A web UI is a network-facing front end to that socket and inherits all of that power, so the project has stayed a small, auditable daemon and left interfaces to others.

### fail2ban GUIs and web UIs compared

| Tool | Type | Can change things | Notes |
|---|---|---|---|
| Webmin, "Fail2Ban Intrusion Detector" module | Server admin panel module | Yes: filters, actions, jails, unban, service | Included in Webmin; under Networking |
| fail2ban-ui (Swissmakers) | Standalone web app (Go), container image | Yes: bans, unbans, jails, filters | Manages several hosts (local socket, SSH, or agent); optional OIDC login |
| fail2ban-dashboard (webishdev) | Web dashboard (Go), container image | Mostly viewing | Reads the socket; basic auth or OAuth2; optional Prometheus metrics |
| fail2web (isavage) | Docker bundle of fail2ban + web UI | Yes | Packaged for docker-compose setups |
| fail2web + fail2rest (Sean-Der) | Web UI over a REST server | Yes | Older project; check its commit history before relying on it |
| Plesk, HestiaCP and other hosting panels | Built into the panel | Yes, within the panel | Plesk calls it "IP Address Banning (Fail2Ban)" |
| FreePBX Intrusion Detection | Built into FreePBX | Yes | Writes fail2ban's config itself. See the [Asterisk guide](/guides/asterisk/) |
| Grafana via an exporter | Dashboards and alerts | No (read-only) | Best for graphs over time and many servers |

Third-party projects change quickly. Look at each repository's recent commits, open issues and license before deploying, and pin a version tag instead of `:latest` in production.

**Webmin** includes a Fail2Ban module, listed as **Fail2Ban Intrusion Detector** under **Networking**. It can list and edit filters (regex and log targets), actions and jails, show each jail's status and active bans with an unblock button, change logging and socket settings, edit the config files directly, and start, stop or restart the service. If it doesn't appear after installing fail2ban, use **Refresh Modules** and check that your Webmin user is allowed to use the module. Webmin itself listens on port 10000 with root-level access to the whole server. Treat it like SSH: restrict it to your IP or VPN and enable two-factor authentication.

**fail2ban-ui** by Swissmakers is a newer, actively developed web interface written in Go. It can manage several fail2ban hosts from one place: on the same host through the socket, over SSH with a key, or through a small agent. It covers reviewing and searching bans, unbanning, managing jails and filters, and notifications, and it supports OIDC login (Keycloak, Authentik, Pocket-ID). It runs as a container (`swissmakers/fail2ban-ui`) under Docker or Podman.

**fail2ban-dashboard** by webishdev is a lightweight Go dashboard that reads the fail2ban socket and shows jails and bans. It supports basic auth and OAuth2 and can expose Prometheus metrics. The container image is `ghcr.io/webishdev/fail2ban-dashboard`. Because the socket is usually root-only, its Docker example runs the container as root with the socket directory mounted.

## Security checklist for any fail2ban web UI

> **A fail2ban UI is root access**
Access to the fail2ban socket lets you define actions that fail2ban runs as root. A web UI with socket access, or a container with `/var/run/fail2ban` mounted, is effectively root on the host. Protect it like one.

1. **Never expose it to the internet.** Bind it to `127.0.0.1` and reach it over an SSH tunnel (`ssh -L 8080:127.0.0.1:8080 server`) or a VPN. If it must sit behind a reverse proxy, restrict the proxy by source IP as well.
2. **Turn on authentication**, even on localhost, and prefer SSO/OIDC or strong unique passwords. Some tools ship with authentication disabled by default.
3. **Use TLS** if it isn't localhost-only, so session cookies and credentials aren't sent in clear text.
4. **Mount the socket read-only where possible** and don't give the container `--privileged` or host networking unless the tool needs them.
5. **Whitelist your admin IP** in fail2ban's [`ignoreip`](/config/whitelist/), so a misclick or a typo in a UI-edited jail can't ban you.
6. **Keep it updated** and watch the project for security advisories. A web UI adds attack surface that fail2ban alone doesn't have.

More hardening advice is on the [security page](/security/).

## DIY: a read-only status page

If you only want to *see* bans in a browser, skip the web app and publish a static page generated by root's crontab. Nothing on the web side can talk to the socket:

```bash
#!/bin/sh
# Write a static, read-only fail2ban status page (fail2ban 1.1.0+ for status --all).
set -eu
OUT=/var/www/f2b-status/index.html
TMP="$OUT.tmp"
{
  echo '<!doctype html><meta charset="utf-8"><title>fail2ban status</title><pre>'
  date
  fail2ban-client status --all | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g'
  echo '</pre>'
} > "$TMP"
mv "$TMP" "$OUT"
```
sudo install -d /var/www/f2b-status
sudo chmod 755 /usr/local/bin/f2b-status-page.sh
echo '* * * * * root /usr/local/bin/f2b-status-page.sh' | sudo tee /etc/cron.d/f2b-status-page
```

## FAQ

### Does fail2ban have a GUI?

No. fail2ban ships only the `fail2ban-server` daemon and the `fail2ban-client` command line. Graphical options come from third parties: Webmin's Fail2Ban Intrusion Detector module, web apps such as fail2ban-ui and fail2ban-dashboard, hosting panels like Plesk, or Grafana dashboards fed by a Prometheus exporter.

### Is there a Prometheus exporter for fail2ban?

Yes. The most used is fail2ban-prometheus-exporter by Hector Smith (`hctrdev/fail2ban-prometheus-exporter` on GitLab). It reads the fail2ban socket and serves `f2b_`-prefixed metrics on port 9191. In Docker, use `registry.gitlab.com/hctrdev/fail2ban-prometheus-exporter:latest` and mount the host directory `/var/run/fail2ban/` read-only, not the socket file.

### Is there a Grafana dashboard for fail2ban?

The exporter project ships one as `_examples/grafana/dashboard.json`. Import it in Grafana under Dashboards → Import (Grafana 9.1.8+). Dashboards on grafana.com often target other exporters with different metric names, so check their queries match `f2b_jail_banned_current` before using them.

### How do I monitor fail2ban with Zabbix?

Use the community fail2ban template listed on Zabbix's integrations page, or add `UserParameter` items that call a small root-owned script through sudo. The script parses `fail2ban-client status` for jail discovery and per-jail banned and failed counts. Also monitor `proc.num[fail2ban-server]` to catch a stopped service.

### Is there a fail2ban web UI for Docker, and is it safe to expose?

fail2ban-ui (`swissmakers/fail2ban-ui`) and fail2ban-dashboard (`ghcr.io/webishdev/fail2ban-dashboard`) run as containers and talk to fail2ban through its socket mounted from the host; fail2web bundles both. Don't expose any of them publicly: socket access is root-equivalent. Bind to localhost, use an SSH tunnel or VPN, and enable authentication.

### What is the best fail2ban dashboard?

For viewing bans over time and across servers, a Prometheus exporter with Grafana is the most robust and read-only. For clicking to unban or edit jails, Webmin's module is the long-standing choice, and fail2ban-ui is a newer multi-host option. For a single server, `fail2ban-client status --all` may be all you need.

### What should I alert on for fail2ban?

First, fail2ban being down or unreachable (`f2b_errors` rising, `fail2ban_up == 0`, or no `fail2ban-server` process), because then nothing is protecting the server. Second, unusual ban surges per jail. Third, a normally busy jail with zero failures for a day, which often means a broken log path or filter.
