How to Self-Host GoAccess for Log Analytics
What Is GoAccess?
GoAccess is a real-time web log analyzer that parses your web server access logs (Nginx, Apache, Caddy) and generates analytics dashboards — either in the terminal or as a self-updating HTML report. No JavaScript tracking snippet required. No cookies. No impact on page load speed. GoAccess reads what your web server already records and turns it into visitor stats, top pages, referrers, geolocation, HTTP status codes, and bandwidth usage.
Updated March 2026: Verified with latest Docker images and configurations.
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 256 MB of free RAM
- Access to your web server’s access log files (Nginx, Apache, Caddy, etc.)
- A domain name (optional, for the HTML dashboard)
Docker Compose Configuration
Create a docker-compose.yml file:
services:
goaccess:
image: allinurl/goaccess:1.10.1
container_name: goaccess
restart: unless-stopped
ports:
- "7890:7890" # HTML report WebSocket (real-time updates)
volumes:
- /var/log/nginx/access.log:/var/log/access.log:ro # CHANGE: path to your access log
- goaccess-data:/srv/data # Persistent database
- goaccess-html:/srv/report # HTML report output
command: >
--log-file=/var/log/access.log
--log-format=COMBINED
--real-time-html
--ws-url=wss://analytics.example.com:7890
--output=/srv/report/index.html
--persist
--restore
--db-path=/srv/data
--anonymize-ip
--geoip-database=/usr/local/share/GeoIP/GeoLite2-City.mmdb
networks:
- analytics
networks:
analytics:
driver: bridge
volumes:
goaccess-data:
goaccess-html:
Key Configuration Options
| Flag | Purpose |
|---|---|
--log-format=COMBINED | Standard Nginx/Apache combined log format. Change to VCOMBINED for virtual host logs, or define a custom format |
--real-time-html | Enables WebSocket-based live updates in the HTML dashboard |
--ws-url | WebSocket URL clients connect to. Set to your domain + port for remote access |
--persist / --restore | Saves parsed data to disk so you don’t lose history on restart |
--anonymize-ip | Hashes the last octet of IP addresses for privacy |
--geoip-database | Path to MaxMind GeoLite2 database for geographic data |
Start the stack:
docker compose up -d
Log Format Configuration
GoAccess supports several preset log formats. Match the format to your web server:
Nginx (default combined)
--log-format=COMBINED
This matches Nginx’s default combined format:
$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
Apache
--log-format=COMBINED
Apache’s combined format is identical to Nginx’s default.
Caddy
Caddy’s default JSON log format requires a custom format string:
--log-format='{"request":{"remote_ip":"%h","method":"%m","uri":"%U","proto":"%H"},"status":"%s","size":"%b","resp_headers":{"Content-Type":["%^"]},"duration":"%T","ts":"%d"}'
Alternatively, configure Caddy to output common/combined format logs, which are easier for GoAccess to parse.
Custom Format
Define your own with GoAccess’s format specifiers:
--log-format='%h %^[%d:%t %^] "%r" %s %b "%R" "%u"'
--date-format=%d/%b/%Y
--time-format=%H:%M:%S
Initial Setup
- Verify GoAccess can read your logs:
docker logs goaccess
Look for lines like Parsed X lines. If you see format errors, your --log-format doesn’t match your actual log format.
- Access the HTML dashboard at
http://your-server-ip:7890 - The dashboard auto-refreshes via WebSocket — no page reload needed
Serving the HTML Report
For production use, serve the generated HTML report through your existing web server instead of exposing the GoAccess WebSocket port directly.
Nginx Configuration
server {
listen 443 ssl;
server_name analytics.example.com;
location / {
root /path/to/goaccess-html;
index index.html;
}
location /ws {
proxy_pass http://localhost:7890;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Mount the goaccess-html volume to a path your Nginx can serve.
See Reverse Proxy Setup for full configuration.
GeoIP Setup
GoAccess can map visitor IPs to countries/cities using MaxMind’s GeoLite2 database:
- Register for a free MaxMind account at maxmind.com
- Download the GeoLite2-City database (
.mmdbformat) - Mount it into the container:
volumes:
- /path/to/GeoLite2-City.mmdb:/usr/local/share/GeoIP/GeoLite2-City.mmdb:ro
- Add the flag:
--geoip-database=/usr/local/share/GeoIP/GeoLite2-City.mmdb
The database should be updated monthly. MaxMind provides a free geoipupdate tool for automated updates.
Terminal Mode
GoAccess also works as a terminal-based dashboard — no web server needed:
docker exec -it goaccess goaccess /var/log/access.log --log-format=COMBINED
This opens an interactive ncurses interface with real-time stats. Useful for quick checks via SSH.
Troubleshooting
”Unable to open log file”
Symptom: GoAccess exits with a permission error on the log file
Fix: Ensure the log file volume mount is correct and the file exists. Check permissions:
ls -la /var/log/nginx/access.log
The container runs as root by default, so file permissions are rarely the issue — more likely the mount path is wrong.
”No valid format found”
Symptom: GoAccess reports 0 parsed lines or format errors
Fix: Your --log-format doesn’t match the actual log format. Check a sample line from your log:
head -1 /var/log/nginx/access.log
Then match it against GoAccess’s format specifiers.
Dashboard Shows No Data After Restart
Symptom: Restarting the container loses all historical data
Fix: Ensure --persist and --restore flags are set, and the goaccess-data volume is properly mounted. Without persistence, GoAccess only shows data from logs loaded since the last start.
WebSocket Connection Fails
Symptom: HTML report loads but shows “Connecting…” without live updates
Fix: The --ws-url must match how clients access the WebSocket. If behind a reverse proxy, ensure WebSocket upgrade headers are passed through. Check that port 7890 is accessible.
Resource Requirements
- RAM: 100-200 MB (depends on log volume and GeoIP database)
- CPU: Minimal — log parsing is not CPU-intensive
- Disk: Negligible for the application. Log files themselves are the main storage cost.
Verdict
GoAccess is the most efficient analytics tool you can run. It adds zero overhead to your website — no tracking scripts, no cookies, no client-side impact. It reads logs your web server already generates and produces clean dashboards.
The trade-off: GoAccess only sees what’s in your access logs. It can’t track JavaScript events, user sessions across pages (without custom configuration), or anything that requires client-side instrumentation. For those needs, use Plausible or Umami.
For a personal site, blog, or any situation where you want analytics without adding a tracking script, GoAccess is ideal. It pairs well with a JavaScript-based tool — run GoAccess for server-side metrics and Plausible for user-facing analytics.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments