# Goaccess

## What Is GoAccess?

[GoAccess](https://goaccess.io/) 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](/foundations/docker-compose-basics))
- 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:

```yaml
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:

```bash
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:

```bash
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.

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

```nginx
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](/foundations/reverse-proxy-explained) 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](https://www.maxmind.com/en/geolite2/signup)
2. Download the GeoLite2-City database (`.mmdb` format)
3. Mount it into the container:

```yaml
volumes:
  - /path/to/GeoLite2-City.mmdb:/usr/local/share/GeoIP/GeoLite2-City.mmdb:ro
```

4. 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:

```bash
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:

```bash
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:

```bash
head -1 /var/log/nginx/access.log
```

Then match it against GoAccess's [format specifiers](https://goaccess.io/man#custom-log-format).

### 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](/apps/plausible) or [Umami](/apps/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.

## Frequently Asked Questions

### Does GoAccess require any JavaScript on my website?
No. GoAccess reads your web server's access log files — it doesn't add anything to your website. Zero impact on page load speed, no cookies, no client-side code. This makes it ideal for privacy-focused analytics.

### Can GoAccess analyze historical log files?
Yes. Point GoAccess at any log file (current or archived) and it parses the entire thing. With `--persist` and `--restore`, it builds a database across multiple log rotations. You can even pipe multiple log files together for a combined view.

### Does GoAccess support multiple websites?
Yes. Run separate GoAccess instances, each reading a different access log file and outputting to a different HTML report. You can also use `VCOMBINED` log format with virtual host logs to show all sites in one dashboard, filtered by hostname.

### How does GoAccess compare to Plausible or Umami?
GoAccess is server-side only — it reads log files and can't track JavaScript events, goals, or user sessions across pages. Plausible and Umami use client-side JavaScript and provide richer analytics (UTM tracking, custom events, real-time dashboards). GoAccess is lighter, more private, and adds zero overhead to your website. Many people run both.

### Can I use GoAccess with Caddy or Traefik?
Yes, but you need to configure these web servers to output logs in a format GoAccess can parse. Caddy's default JSON format requires a custom format string. The simplest approach is configuring Caddy/Traefik to output combined-format logs, which GoAccess parses natively.

### Is GeoIP data required?
No. GeoIP is optional. Without it, GoAccess shows all analytics except geographic location data. For geo data, download the free MaxMind GeoLite2 database and mount it into the container.

## Related

- [GoAccess vs Plausible: Which Analytics to Self-Host?](/compare/goaccess-vs-plausible/)
- [GoAccess vs Umami: Self-Hosted Analytics Compared](/compare/goaccess-vs-umami/)
- [Best Self-Hosted Analytics](/best/analytics)
- [How to Self-Host Plausible](/apps/plausible)
- [How to Self-Host Umami](/apps/umami)
- [How to Self-Host Matomo](/apps/matomo)
- [Plausible vs Umami](/compare/plausible-vs-umami)
- [Replace Google Analytics](/replace/google-analytics)
- [Reverse Proxy Setup](/foundations/reverse-proxy-explained)
- [Docker Compose Basics](/foundations/docker-compose-basics)
