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

FlagPurpose
--log-format=COMBINEDStandard Nginx/Apache combined log format. Change to VCOMBINED for virtual host logs, or define a custom format
--real-time-htmlEnables WebSocket-based live updates in the HTML dashboard
--ws-urlWebSocket URL clients connect to. Set to your domain + port for remote access
--persist / --restoreSaves parsed data to disk so you don’t lose history on restart
--anonymize-ipHashes the last octet of IP addresses for privacy
--geoip-databasePath 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

  1. 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.

  1. Access the HTML dashboard at http://your-server-ip:7890
  2. 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:

  1. Register for a free MaxMind account at maxmind.com
  2. Download the GeoLite2-City database (.mmdb format)
  3. Mount it into the container:
volumes:
  - /path/to/GeoLite2-City.mmdb:/usr/local/share/GeoIP/GeoLite2-City.mmdb:ro
  1. 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.

Comments