GoAccess
Real-time web log analyzer and interactive viewer
GoAccess is a fast, terminal-based and web-based real-time log analyzer for web server logs. It parses access logs and renders interactive reports of traffic, visitors, and HTTP statistics.
Key features
- Real-time log parsing
- Terminal and HTML reports
- No database required
- Supports many log formats
Pros & cons
Strengths
- Extremely fast and lightweight
- Works fully offline
Trade-offs
- Log-file driven only
- Limited historical aggregation
GoAccess replaces
Last reviewed Aug 26, 2026 · 777 words
goaccess /var/log/nginx/access.log --log-format=COMBINED \
-o /var/www/stats/index.html --real-time-html --daemonize
That single line is the whole product: an HTML report that updates itself every second from the log nginx was already writing, with no JavaScript snippet on your pages, no database, and a process that uses about 20 MB of RAM on a busy site. GoAccess is 15 years old, written in C, and it is the analytics tool I put on every server before deciding whether the site deserves anything heavier.
No tracking script means you finally see the bots
Script-based analytics like Plausible and Umami count browsers that run JavaScript, which is to say humans without ad blockers. Server logs count every request: crawlers, scrapers, the AI bots hammering your sitemap, the 404 flood from someone scanning for /wp-login.php, and the actual visitors. On a typical small site GoAccess reports 3 to 10 times the request volume Plausible reports, and the difference is the part you need to see for capacity, blocking and SEO. The flip side is that it cannot tell you about sessions, scroll depth or which button was clicked; for that you still want a script-based tool, and the ditching Google Analytics piece covers pairing the two.
Three outputs from one binary
Run it with no -o and you get an ncurses dashboard in the terminal, useful for "what is hitting me right now" over SSH. Add -o report.html for a static page you can regenerate from cron. Add --real-time-html and the page opens a WebSocket to GoAccess, on port 7890 by default, and refreshes live. Behind a reverse proxy that last mode needs one extra flag, --ws-url=wss://stats.example.com:443/ws, and a proxy route for /ws to localhost:7890, or the page loads once and never updates. -o report.json gives you the same data for scripts.
Persistence is opt-in, and you want it
Without flags, GoAccess only knows about the file it is reading, so log rotation wipes your history every week. Two flags fix that:
zcat -f /var/log/nginx/access.log* | goaccess - --log-format=COMBINED \
--persist --restore --db-path=/var/lib/goaccess \
-o /var/www/stats/index.html
--persist writes the parsed state to an on-disk store and --restore loads it next time, so a nightly cron over the rotated files builds up months of data. The "limited historical aggregation" the catalogue flags is real: you get totals and a daily series, not a query engine, and the store grows with unique visitors. For a personal site that is a few hundred MB after a year; for a site doing millions of hits it is where you outgrow GoAccess.
Log formats: nginx just works, Caddy and Traefik need a format string
COMBINED matches nginx and Apache defaults exactly. Caddy writes JSON access logs, and recent GoAccess releases include a CADDY preset for them; on older versions you describe the JSON keys with a custom --log-format. Traefik's default access log looks like Common Log Format but appends request count, router name and duration, so the built-in preset stops matching at the tail and you need a custom format that names those fields. The GoAccess manual has working strings for all of these; the mistake to avoid is guessing, because a wrong format silently parses zero lines rather than erroring.
Docker, GeoIP and the two-minute polish
The official allinurl/goaccess image takes the same flags; mount the log directory read-only and the output directory read-write, and point a --config-file at a mounted goaccess.conf so the command line stays short. For the geography panel, download MaxMind's free GeoLite2-City database (free account required) and pass --geoip-database=/etc/goaccess/GeoLite2-City.mmdb. Two config lines worth adding: ignore-crawlers true to hide known bots from the visitor counts once you have looked at them, and exclude-ip 192.168.1.0-192.168.1.255 so your own browsing stops inflating the numbers.
What I'd do
GoAccess with --persist --restore in a nightly cron on every web server, report published at a private path behind your reverse proxy's auth, GeoLite2 for the map panel, crawlers visible for the first month and hidden after. Add Plausible or Umami only on the one site where you genuinely care about human behaviour rather than traffic. The whole thing costs 20 MB of RAM, zero cookies, and nothing on the page, which is the right default for everything you host.
Similar monitoring & status apps
Uptime Kuma
Monitoring & StatusEasy self-hosted uptime monitoring tool
Replaces Pingdom, UptimeRobot
Netdata
Monitoring & StatusReal-time per-second infrastructure monitoring
Replaces Datadog, New Relic
Grafana
Monitoring & StatusOpen observability dashboards and visualization
Replaces Datadog
Prometheus
Monitoring & StatusMetrics-based monitoring and alerting toolkit
Replaces Datadog
Glances
Monitoring & StatusCross-platform system monitoring at a glance
Replaces Datadog
InfluxDB
Monitoring & StatusPurpose-built time series database for metrics and events
Replaces Datadog, AWS Timestream