This project provides a lightweight web-based control panel for managing Fail2ban. It allows you to:
- View the global status of Fail2ban
- See active jails and their details
- Ban/unban IP addresses
The application is containerized with Docker and connects directly to the fail2ban.sock socket file shared from your running Fail2ban instance.
There are two installation options:
You must share the Fail2ban socket file with this container so it can communicate with the Fail2ban service.
Mount the directory containing the socket from inside the fail2ban container. if fail2ban is installed in the host-system you can skip this part and mount the path directly,
volumes:
- /path/to/directory:/var/run/fail2ban
- ./path/to/logfile:/path/in/container/logfiledocker-compose.dockerhub (rename it to docker-compose) example:
version: '3.9'
services:
fail2bancontrol:
image: oweitman/fail2bancontrol
container_name: fail2bancontrol
ports:
- '9191:9000'
volumes:
# Include sock file from host (adjust path!)
- ./path/to/socket.sock:/path/in/container/socket.sock
# Include log file from host (adjust path!)
- ./path/to/logfile:/path/in/container/logfile
environment:
TZ: Europe/Berlin
restart: unless-stoppedStart:
docker compose up -ddocker run -d \
--name fail2bancontrol \
-p 9191:9000 \
-e TZ=Europe/Berlin \
-v /path/to/directory:/var/run/fail2ban \
-v /path/to/logfile:/path/in/container/logfile \
--restart unless-stopped \
oweitman/fail2bancontrolOpen:
http://<host>:9191
git clone https://github.com/oweitman/fail2bancontrol fail2bancontrol
cd fail2bancontrolCreate a file build-image.sh in the repository root with the following content:
#!/usr/bin/env bash
docker build -t fail2bancontrol .Make it executable and run:
chmod +x build-image.sh
./build-image.shYou must share the Fail2ban socket file with this container so it can communicate with the Fail2ban service.
Mount the directory containing the socket from inside the fail2ban container. if fail2ban is installed in the host-system you can skip this part and mount the path directly,
volumes:
- /path/to/directory:/var/run/fail2ban
- ./path/to/logfile:/path/in/container/logfiledocker-compose.local (rename it to docker-compose) example:
version: '3.9'
services:
fail2bancontrol:
image: fail2bancontrol:latest
container_name: fail2bancontrol
ports:
- '9191:9000'
volumes:
# directory mount
- '/path/to/directory:/var/run/fail2ban'
- ./path/to/logfile:/path/in/container/logfile
environment:
TZ: Europe/Berlin
restart: unless-stoppedStart:
docker compose up -ddocker run -d \
--name fail2bancontrol \
-p 9191:9000 \
-e TZ=Europe/Berlin \
-v /path/to/directory:/var/run/fail2ban \
-v /path/to/logfile:/path/in/container/logfile \
--restart unless-stopped \
fail2bancontrol:latestOpen:
http://<host>:9191
- Number of current and total number of blocked and failed IPs
- All referred Files of this jail.
- all current banned IPs.
- Unban an IP address
- Ban an IP address
- Extra infos (e.g. maxmatches, maxlines, maxretry, findtime, bantime)
If the respective log file has been mapped to the fail2bancontrol container sld volume with the same path, this log file can be displayed and continuously monitored.
Example:
volumes:
- ./path/to/socket.sock:/path/in/container/socket.sock
- ./path/to/logfile:/path/in/container/logfileErrors
The API uses standard HTTP codes. Error body shape:
{ "error": "<message>" }Global Fail2ban status.
200
{
"jails": 3,
"list": ["sshd", "nginx-http-auth", "recidive"]
}500 Socket/call failure.
Array of jail names (same as status.list).
200
["sshd", "nginx-http-auth", "recidive"]500 On failure.
Detailed status for a single jail.
Path params
jail— exact jail name
200
{
"filter": {
"currentlyFailed": 2,
"totalFailed": 431,
"fileList": [
{ "path": "/var/log/auth.log", "exists": true },
{ "path": "/var/log/secure", "exists": false }
]
},
"actions": {
"currentlyBanned": 1,
"totalBanned": 37,
"bannedIPList": ["1.2.3.4"]
},
"extra": {
"maxlines": "1",
"maxmatches": "8",
"maxretry": "8",
"findtime": "600",
"bantime": "7200"
}
}500 Unknown jail or socket error.
Collects all banned IPv4 addresses across Fail2ban output.
200
{ "ips": ["1.2.3.4", "1.2.3.5"], "count": 2 }Ban a single IPv4 in a jail.
Body
{ "ip": "1.2.3.4" }200
{ "result": "<fail2ban textual response>" }400 Invalid/missing IPv4. 500 Socket/fail2ban error.
Unban a single IPv4 in a jail.
Body
{ "ip": "1.2.3.4" }200
{ "result": "<fail2ban textual response>" }400/500 As above.
-
POST
/api/unban/all→ runsunban --all-
200
{ "result": "<response>", "command": ["unban", "--all"] }
-
-
POST
/api/unban/{ip}→ runsunban <ip>-
200
{ "result": "<response>", "command": ["unban", "1.2.3.4"] } -
400 if
{ip}is not a valid IPv4.
-
-
POST
/api/unbanwith body{ "ip": "1.2.3.4" }-
200
{ "result": "<response>", "command": ["unban", "1.2.3.4"] } -
400 invalid/missing IPv4.
-
Note: IPv6 is not accepted by these endpoints.
200
{ "result": "<fail2ban textual response>" }Reload with optional flags.
Body (all optional booleans)
{ "restart": false, "unban": false, "all": false }Maps to:
--restartifrestart: true--unbanifunban: true--allifall: true
200
{
"result": "<response>",
"command": ["reload", "--restart", "--all"] // example
}Body (optional)
{ "unban": false, "ifExists": false }→ restart [--unban] [--if-exists] <JAIL>
200
{
"result": "<response>",
"command": ["restart", "--if-exists", "sshd"]
}Body (optional)
{ "restart": false, "unban": false, "ifExists": false }→ reload [--restart] [--unban] [--if-exists] <JAIL>
200
{
"result": "<response>",
"command": ["reload", "--restart", "sshd"]
}The following parameters are possible as {setting}
- findtime
- bantime
- maxretry
- maxmatches
- maxlines
Body
{ "value": 10 }200
{ "result": "<response>" }400 Missing or non-integer value.
Returns Fail2ban version (raw textual response collapsed to a string).
200
{ "version": "1.1.0" }Also available as POST
/api/version(same response).
200
{ "loglevel": "loglevel\nINFO" }Many “get” responses are raw Fail2ban text, often a name line + value line. In clients you may need to split on
\nand read the second line.
Set log level.
Body
{ "level": "INFO" } // or { "level": 20 }Accepted values are those supported by Fail2ban:
CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG, TRACEDEBUG, HEAVYDEBUG or numeric 50..5.
200
{ "result": "<response>", "command": ["set", "loglevel", "INFO"] }400 Missing/invalid level.
200
{ "dbfile": "dbfile\n/var/lib/fail2ban/fail2ban.sqlite3" }200
{ "dbmaxmatches": "dbmaxmatches\n10" }Body
{ "value": 10 }200
{ "result": "<response>" }400 Missing or non-integer value.
200
{ "dbpurgeage": "dbpurgeage\n86400" }Body
{ "seconds": 86400 }200
{ "result": "<response>" }400 Missing or non-integer seconds.
Read a file from the host filesystem.
Query
-
path(required) — absolute or relative (resolved to absolute) -
lines(optional, integer)0or omitted → entire file- positive
n→ firstnlines - negative
-n→ lastnlines
200
{
"path": "/var/log/nginx/access.log",
"exists": true,
"lines": ["<line 1>", "<line 2>", "..."]
}404 File not found.
500 I/O or decoding error.
Security note: The path is resolved to an absolute path and must exist and be a regular file. There is no allow-list; protect this endpoint appropriately (e.g., via reverse proxy auth/ACLs).
/→ servesindex.htmlfromSTATIC_ROOT/public/<path>→ serves fromSTATIC_ROOT/<path>Unknown static paths →404.
- IP validation is IPv4 only for ban/unban endpoints.
- Several “get” endpoints return raw textual Fail2ban output collapsed into a single string; clients often need to split lines and use the second line for the value.
- All commands are executed through the Fail2ban UNIX socket defined by
F2B_SOCKET.
-
Permission denied: If you get this error, your container user may not have permissions to read the socket. Quick fix: run the container as
root(default in Dockerfile). Alternative: adjust socket file permissions or match the group ID inside the container. -
Verify the socket inside the container:
docker exec -it fail2bancontrol ls -l /var/run/fail2ban -
Port mapping: The internal app port is controlled by
PORT(default9000). External port is defined in Docker Compose ordocker run(9191:9000in examples).
- optimize overview requests
- add short time caching for some values
- more error details in api responses
- add jail extra infos in frontend and api
- little css improvement
- add more api endpoints
- add server control buttons start, stop, restart, reload
- change theme switch button
- add server version
- add dbfile location
- add log level slider
- add dbmaxmatches
- add dbpurgeage
- add eslint
- fix eslint errors
- improve refresh between overview and jails
- improve design
- add testscript for banned ips
- add some more documentations
- recreate frontend sources
- improve release script
- add version
- add workflow actions for release and version
- improve backend logic für prod and dev
- add footer with links
- automate docker push
- Feature FileView
- Move gui to mui/react
- Initial release
