Real-time log viewer for Apache, Nginx, NPM, system logs and Fail2ban
README en FranΓ§ais | Installation | Plugins | Configuration | Reverse Proxy | Documentation
- Installation
- Plugins
- Configuration
- Reverse Proxy
- System Log Access
- Documentation
- Contribution
- License
LogviewR - real-time log viewer for Apache, Nginx, NPM, system logs and Fail2ban.
- π Real-time via WebSocket
- π Filters: level, date, IP, HTTP methodβ¦
- π Statistics and dashboards per plugin
- π JWT auth, role management
- π³ Docker-ready
π₯οΈ Host System - Linux/Unix system logs
- Syslog, auth, kernel, daemon, mail, custom logs
- Automatic Docker environment detection
- RFC 3164 / RFC 5424 support
- Configurable base path (
/var/logor/host/logsin Docker)
π Apache - Apache HTTP Server logs
- Access logs (Combined, Common, VHost) + Error logs
- IP, timestamp, HTTP method, status code, referer, user-agent extraction
- Editable default regex,
.gzsupport
π Nginx - Nginx logs
- Access logs (Combined, Common, Main, Extended) + Error logs
- Timestamp parsing with timezone handling
- Fail2ban and ELK compatible regex,
.gzsupport
π Nginx Proxy Manager (NPM) - NPM logs
- 5 supported formats with automatic detection
- Fields: cache, upstream status, gzip ratio, subdomains
.gzsupport
π‘οΈ Fail2ban - jail monitoring and banned IPs
Tabs: Jails Β· Filters Β· Actions Β· IP Tracker Β· Map Β· Ban Manager Β· Stats Β· IPTables Β· IPSet Β· NFTables Β· Config Β· Audit
Requirements: fail2ban installed and active on the host. Host setup required - see Installation Step 2.
To verify: Administration β Plugins β Fail2ban β Diagnostic.
Firewall tabs in Docker (IPTables Β· IPSet Β· NFTables)
These tabs require two cumulative conditions - neither alone is sufficient:
| Condition | Role |
|---|---|
network_mode: host |
Shares host network namespace - container sees host iptables/ipset/nft rules |
cap_add: NET_ADMIN |
Linux capability required by the kernel for netfilter read/write |
β οΈ Three incompatibilities to know:
network_mode: hostis incompatible withports:- removeports:and usePORT=7500inenvironment:insteadsecurity_opt: no-new-privileges:trueis incompatible with firewall tabs -sudocannot elevate with this flag, breaking iptables/ipset/nft commands- To change the listen port: set
PORT=8080in.envand point your reverse proxy to127.0.0.1:8080
Use docker-compose.fail2ban.yml β it includes network_mode: host, NET_ADMIN, and fail2ban socket/group already configured. See Installation Step 2.
Without these options, IPTables/IPSet/NFTables tabs will show a Permission denied or no new privileges error.
Fail2ban is optional. LogviewR works out of the box for viewing Apache, Nginx, NPM and system logs β no extra setup needed. The Fail2ban plugin is a powerful addition that lets you fully manage fail2ban (jails, bans, IPSet lists, firewall rules) from the dashboard, but it is not required.
Step 1 - Create the application directory
mkdir -p /home/docker/logviewr && cd /home/docker/logviewrStep 2 - Create .env and choose your docker-compose file
echo "JWT_SECRET=$(openssl rand -base64 32)" > .envStandard β log viewer only (Apache, Nginx, NPM, system logs):
wget -O docker-compose.yml https://raw.githubusercontent.com/Erreur32/LogviewR/main/docker-compose.ymlFail2ban + Firewall β full fail2ban management + IPTables/IPSet/NFTables tabs:
wget -O docker-compose.yml https://raw.githubusercontent.com/Erreur32/LogviewR/main/docker-compose.fail2ban.yml
# then run the setup script (one-time, fixes host-side permissions):
curl -fsSL https://raw.githubusercontent.com/Erreur32/LogviewR/main/scripts/setup-fail2ban-access.sh | sudo bashThe setup script automatically creates the
fail2bangroup, sets socket/SQLite permissions, and installs a systemd drop-in for persistence across reboots. The container itself auto-detects and joins the socket's owning group at startup, so no.envgroup ID is required. Run it once on the Docker host β survives reboots automatically.
Step 3 - Start
docker compose up -dDashboard available at http://your-ip:7500
| Variable | Description | Default | Required |
|---|---|---|---|
JWT_SECRET |
Secret used to sign JWT tokens | - | β Yes |
DASHBOARD_PORT |
Dashboard port (bridge mode with ports:) |
7500 |
No |
PORT |
Direct listen port (network_mode: host mode) |
3000 |
No |
HOST_IP |
Host machine IP address | Auto-detect | No |
CONFIG_FILE_PATH |
Path to external configuration file | /app/config/logviewr.conf |
No |
ADM_GID |
GID of the adm group on the host (system logs) |
4 |
No |
HOST_ROOT_PATH |
Host root path mounted in the container | /host |
No |
TZ |
Container timezone β must match your host TZ so log timestamps (written by Apache/Nginx in host local time, without TZ info) are parsed correctly. Override if your host is not in Europe/Paris. | Europe/Paris |
No |
Two ready-to-use files β download the one that matches your setup:
| File | Mode | What it does |
|---|---|---|
docker-compose.yml |
Standard | Log viewer only (Apache, Nginx, NPM, system). Bridge network, ports: mapping. |
docker-compose.fail2ban.yml |
Fail2ban + Firewall | Full fail2ban management + IPTables/IPSet/NFTables tabs. network_mode: host + NET_ADMIN. Requires setup-fail2ban-access.sh. |
See Installation Step 2 for download commands.
Fail2ban optional rw mounts (fail2ban mode only): The host filesystem is mounted
:rofor security. Two features need a dedicated rw bind mount (uncomment indocker-compose.fail2ban.yml):
Feature Uncomment source:SQLite VACUUM (Fail2ban Config tab) /var/lib/fail2banConfig file editing from the UI ( jail.local/fail2ban.local)/etc/fail2banShort-form mounts cannot override a
:roparent β the long-form syntax withpropagation: sharedis required.
Changing the port:
- Standard mode: set
DASHBOARD_PORT=8080in.env - Fail2ban mode: set
PORT=8080in.env, then point your reverse proxy to that port
When using fail2ban mode (network_mode: host), there is no Docker port mapping β the container listens directly on the host. A reverse proxy connects via 127.0.0.1:
In standard mode (ports: mapping), a reverse proxy can connect to 127.0.0.1:7500 the same way, or you can expose the port directly without a proxy.
Forward Hostname : 127.0.0.1
Forward Port : 7500 β must match PORT= or DASHBOARD_PORT=
server {
listen 443 ssl;
server_name logviewr.example.com;
location / {
proxy_pass http://127.0.0.1:7500;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}logviewr.example.com {
reverse_proxy 127.0.0.1:7500
}
http:
routers:
logviewr:
rule: "Host(`logviewr.example.com`)"
service: logviewr
services:
logviewr:
loadBalancer:
servers:
- url: "http://127.0.0.1:7500"The Host System plugin reads log files owned by root:adm (permissions 640).
The container automatically joins the adm group (GID 4) via group_add in docker-compose.
If your system uses a different GID for the adm group:
getent group adm | cut -d: -f3 # check the GID on the host
echo "ADM_GID=your_gid" >> .envSome files (/var/log/php8.0-fpm.log, /var/log/rkhunter.log) are owned by root:root 600 and are not readable even with adm group membership.
Fix them on the host:
sudo chgrp adm /var/log/php8.0-fpm.log* && sudo chmod 640 /var/log/php8.0-fpm.log*To make this persist across log rotation, add to /etc/logrotate.d/php8.0-fpm:
create 640 root adm
- MCP Server - control fail2ban and query logs from an AI agent (Claude Code, Claude Desktop, etc.)
β οΈ With the default approval mode, agent bans run without human approval. If you reach your server from outside, add your public IP to the trusted list (Settings > Analysis) or set the MCP approval mode to "all writes", otherwise a manipulated agent could ban your own IP. See Prompt injection and write actions.
- Log Analytics - architecture and data flow of the analytics dashboard
- Environment variables - full reference, per execution mode
- UniFi Controller setup - configuring the UniFi plugin
- How Docker log access works - the
/:/host:romount explained - HOST_ROOT_PATH - accessing host files from inside the container
- docker-compose files compared - which file to use and when
- Testing Docker locally - simulate production before deploying
- Resetting a production deployment - clean slate procedure
- Production troubleshooting - WebSocket and common errors
- Fixing log permissions -
root:adm640 files unreadable - Fixing 401 errors - JWT secret misconfiguration
- Fixing a Docker mount issue - volume path mismatch
- Nginx WebSocket configuration - "Invalid frame header" fix
- Parser guides - supported formats and regex
- NPM Parser Help - NPM formats
- Nginx Parser Help - Nginx formats
- Host-system integration audit - error/warning scan
Always follow the full pre-push checklist from CLAUDE.md β never skip steps.
Why: Multiple versions were pushed in rapid succession during the 2026-04-15 session (v0.8.48β0.8.53), and SonarCloud/rate-limit issues were only caught after push. User wants checks BEFORE push.
How to apply:
- Run
npx tsc+npm run test:runbefore every commit - Check SonarCloud patterns (accessibility, replaceAll, log injection) before push
- Group related fixes into one version instead of pushing many small ones
- Use
docker-compose.local.ymlwith--buildto test before publishing ghcr.io image - The prod database is at
/home/docker/LogviewR/data/dashboard.dbβ copy it into the local container for testing with real data - Run commands yourself instead of giving the user copy-paste instructions
Patch pushes without version bump are OK β user confirmed 2026-04-17. For internal refactors / CodeQL / Sonar fixes that don't warrant a release, push straight to main. Consequence: the current version tag on GHCR (:0.8.x) gets overwritten each push, so :0.8.56 becomes mouvant. Acceptable for this personal project; only bump package.json when there's a batch of user-visible changes worth announcing.
Patch bumps preferred even for feature batches β 2026-04-18 session: I proposed 0.9.0 β 0.10.0 for a UX bundle (NPM domain badge, auto-discover, IP exclusion UX). User corrected to 0.9.1: "0.9.1, pas de version majeur pour Γ§a". Default to patch bumps on the 0.x.y line; reserve minor bumps (0.y.0) for real milestones. Don't assume semver-minor just because features were added.
When running pre-push checks, DO NOT git add -A blindly. User keeps local changes in the working tree that aren't meant to be committed (e.g. docker-compose.yml swapped to a variant for local testing). Use git add -A ':!docker-compose.yml' or stage explicitly to avoid pulling in unrelated edits. Surface unexpected modifications to the user before staging.
Prefer in-repo implementations over new dependencies. Session 2026-04-17 rejections: Sonner (toasts β custom notificationStore already covers the need), motion/framer-motion (CSS transitions + existing keyframes suffice), Tremor (palette conflict + replaces 1682 lines of working SVG), visx (same). Don't propose libs prophylactically; only suggest when a specific feature genuinely needs one.
gh CLI is authenticated with repo scope β can set repo secrets (gh secret set NAME --body "..."), trigger workflows, etc. Use it directly instead of asking the user to click through the GitHub UI.
If a version tag is already pushed to origin and more fixes need to go out under "the same release," bump to the next patch version instead of force-moving the existing tag. Session 2026-08-15: mid-v0.9.18 push, a new fix arrived after v0.9.18 was already tagged and pushed on an earlier commit. Asked the user whether to force-move the tag (git tag -f + force-push) or bump to v0.9.19; user rejected the question prompt and just said "avec nouveau bump version" β bump and move on. Why: force-moving a published tag rewrites a ref others may have already fetched; a patch bump is strictly additive and needs no force-push. How to apply: don't offer tag force-move as the default option β just bump to the next patch version when new work lands after a tag/push, unless the user explicitly asks to amend that specific release.
Contributions are welcome!
This project is licensed under the MIT License. See LICENSE.
Made with β€οΈ for system administrators and developers
Issues | Discussions | Wiki