Install GoAccess: Real-time Nginx Log Analysis on VPS
GoAccess is an open-source web log analyzer that works entirely in-memory — no Elasticsearch, no Logstash, no Kibana required. It runs as an interactive terminal dashboard or exports a self-contained HTML report with optional WebSocket live updates. I've run it on lightweight VPS instances for years: under 10 MB RAM, instant startup, and it immediately shows me which bots are hammering my server, which endpoints are throwing 404s, and whether traffic patterns look normal. For small-to-medium VPS deployments, it's the perfect lightweight alternative to the full ELK stack.
Why GoAccess Instead of ELK or Grafana Loki?
| Factor | GoAccess | ELK Stack | Grafana Loki |
|---|---|---|---|
| RAM Usage | < 10 MB | 2–8 GB+ | 500 MB+ |
| Setup | One command | Complex | Moderate |
| Real-time | ✅ WebSocket | ✅ (Kibana) | ✅ (Grafana) |
| Cost | Free | Free, but high spec required | Free, but high spec required |
| Best For | Small–medium VPS | Enterprise / large-scale logging | Multi-service observability |
Install GoAccess on Ubuntu/Debian
Ubuntu 22.04/24.04 ships an older GoAccess version in its default repositories. Use the official repo for the latest release:
# Add GoAccess official repository
echo "deb https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/goaccess.list
wget -O - https://deb.goaccess.io/gnugpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/goaccess.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/goaccess.gpg] https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/goaccess.list
sudo apt update
sudo apt install -y goaccess
# Verify
goaccess --version
Install on CentOS/Rocky Linux/AlmaLinux
# Install from EPEL
sudo dnf install -y epel-release
sudo dnf install -y goaccess
goaccess --version
Understanding Your Nginx Log Format
Before running GoAccess you need to know what log format Nginx uses. Check /etc/nginx/nginx.conf:
# View the log_format directive
grep -A5 "log_format" /etc/nginx/nginx.conf
# Default combined format:
# $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
# Find access_log paths across all configs
grep "access_log" /etc/nginx/nginx.conf /etc/nginx/sites-enabled/* 2>/dev/null | head -10
Run GoAccess Real-time in Terminal
The fastest way to get started — open a terminal and run:
# Analyze Nginx access log with COMBINED format
sudo goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
-c
# Skip the format dialog and specify directly
sudo goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
--no-query-string
# Analyze multiple log files (current + rotated)
sudo goaccess /var/log/nginx/access.log /var/log/nginx/access.log.1 \
--log-format=COMBINED
The terminal dashboard is divided into panels: Visitors, Requests, Static Requests, Not Found (404s), Hosts, Operating Systems, Browsers, Time Distribution, and Virtual Hosts.
F5 to refresh, number keys (1–9) to expand any panel, q to quit.Generate an HTML Dashboard with Cron
The feature I use most — export a self-contained HTML report viewable in any browser, with optional WebSocket live updates:
Static HTML Report
# Create the output directory
sudo mkdir -p /var/www/html/analytics
sudo chown www-data:www-data /var/www/html/analytics
# Generate report
sudo goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
--output=/var/www/html/analytics/index.html \
--no-query-string
ls -lh /var/www/html/analytics/index.html
Auto-update via Cron (every hour)
# Edit root crontab
sudo crontab -e
# Add this line
0 * * * * goaccess /var/log/nginx/access.log --log-format=COMBINED --output=/var/www/html/analytics/index.html --no-query-string 2>/dev/null
Protect the Dashboard with HTTP Basic Auth
# Create password file
sudo apt install -y apache2-utils
sudo htpasswd -c /etc/nginx/.analytics-htpasswd admin
# Add location block in Nginx config
location /analytics/ {
auth_basic "Analytics";
auth_basic_user_file /etc/nginx/.analytics-htpasswd;
alias /var/www/html/analytics/;
}
# Reload Nginx
sudo nginx -t && sudo systemctl reload nginx
Real-time WebSocket Server Mode
GoAccess can run as a daemon that pushes live updates via WebSocket, so the HTML dashboard refreshes automatically without a page reload:
# Run GoAccess daemon with WebSocket
sudo goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
--output=/var/www/html/analytics/index.html \
--real-time-html \
--ws-url=wss://yourdomain.com/ws \
--port=7890 \
--daemonize \
--pid-file=/var/run/goaccess.pid \
--log-file=/var/log/goaccess.log
# Verify daemon is running
ps aux | grep goaccess
Nginx WebSocket Proxy Config
# Add inside your Nginx server block
location /ws {
proxy_pass http://127.0.0.1:7890;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_read_timeout 7200;
}
Filtering and Custom Log Formats
# Analyze only today's requests (pipe from grep)
grep "27/Aug/2026" /var/log/nginx/access.log | \
sudo goaccess --log-format=COMBINED -
# Inspect suspicious IP range
grep "^185.220." /var/log/nginx/access.log | \
sudo goaccess --log-format=COMBINED -
# Include all rotated gzip logs
(sudo zcat /var/log/nginx/access.log.*.gz 2>/dev/null; sudo cat /var/log/nginx/access.log) | \
goaccess --log-format=COMBINED -o /var/www/html/analytics/index.html -
Custom Log Format
# If Nginx uses a non-standard format, specify it explicitly
sudo goaccess /var/log/nginx/access.log \
--log-format='%h - %e [%d:%t %^] "%r" %s %b "%R" "%u" "%^"' \
--date-format='%d/%b/%Y' \
--time-format='%H:%M:%S'
Create a systemd Service for GoAccess Daemon
# Create service file
sudo tee /etc/systemd/system/goaccess.service << 'EOF'
[Unit]
Description=GoAccess Real-time Web Analytics
After=network.target nginx.service
[Service]
Type=forking
User=root
ExecStart=/usr/bin/goaccess /var/log/nginx/access.log \
--log-format=COMBINED \
--output=/var/www/html/analytics/index.html \
--real-time-html \
--ws-url=wss://yourdomain.com/ws \
--port=7890 \
--daemonize \
--pid-file=/var/run/goaccess.pid \
--log-file=/var/log/goaccess.log
ExecStop=/bin/kill -TERM $MAINPID
PIDFile=/var/run/goaccess.pid
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable goaccess
sudo systemctl start goaccess
sudo systemctl status goaccess
Verify GoAccess Is Working Correctly
# Check version and build options
goaccess --version
# Dry-run to test log parsing without output
sudo goaccess /var/log/nginx/access.log \
--log-format=COMBINED --dry-run 2>&1 | tail -5
# Check daemon service status
systemctl status goaccess
# Verify WebSocket port is listening
ss -tlnp | grep 7890
# Count total requests in current log
sudo wc -l /var/log/nginx/access.log
sudo goaccess /var/log/nginx/access.log -c to open the format selection dialog — this tells you exactly which format matches your actual log entries.Troubleshooting Common Errors
Error: "No input data was provided"
GoAccess can't read the log file — almost always a permissions issue.
# Check file permissions
ls -la /var/log/nginx/access.log
# Run with sudo
sudo goaccess /var/log/nginx/access.log --log-format=COMBINED
# Or add current user to adm group (Debian/Ubuntu)
sudo usermod -aG adm $USER
# Log out and back in
Error: "Couldn't parse log on date/time" or many invalid entries
The specified log format doesn't match the actual log file format. Inspect real log entries and adjust.
# View first 3 lines of the actual log
sudo head -3 /var/log/nginx/access.log
# Expected combined format:
# IP - user [dd/Mon/YYYY:HH:MM:SS +zone] "METHOD /path HTTP/x" status bytes "referer" "UA"
# Use custom format specifiers if needed
sudo goaccess /var/log/nginx/access.log \
--log-format='%h %^ %^ [%d:%t %^] "%r" %s %b "%R" "%u"' \
--date-format='%d/%b/%Y' \
--time-format='%H:%M:%S'
Error: "WebSocket connection failed" on HTML dashboard
Nginx is missing the WebSocket proxy configuration, or the firewall is blocking GoAccess's port.
# Verify GoAccess daemon is listening
ss -tlnp | grep 7890
# Check Nginx config for WebSocket proxy
grep -rn "proxy_pass.*7890\|Upgrade" /etc/nginx/sites-enabled/
# Check firewall
sudo ufw status | grep 7890
Error: "Output file already opened by another instance"
Another GoAccess process is already writing to the same output file. Stop the old instance first.
# Find all GoAccess processes
ps aux | grep goaccess
# Stop via systemd
sudo systemctl stop goaccess
# Or kill directly
sudo kill $(cat /var/run/goaccess.pid)
sudo systemctl start goaccess
HTML report shows sparse data or empty panels
The log file just started, or rotated logs aren't included. Combine all rotated logs.
# Include all rotated gzip logs
(sudo zcat /var/log/nginx/access.log.*.gz 2>/dev/null; sudo cat /var/log/nginx/access.log) | \
goaccess --log-format=COMBINED -o /var/www/html/analytics/index.html -
Frequently Asked Questions
What log formats does GoAccess support?
GoAccess supports COMBINED (Nginx/Apache combined log), COMMON (Apache common), VCOMBINED (when Nginx sits behind a load balancer that appends the real IP), W3C (IIS), CLOUDFRONT, CLOUDSTORAGE, AWSELB, SQUID, AWSS3, and fully custom formats defined with strftime-style specifiers. Most Nginx and Apache deployments work out of the box with --log-format=COMBINED.
Is the GoAccess HTML dashboard secure to expose publicly?
GoAccess processes everything in-memory — no database, no persistent data files (aside from the HTML output you explicitly generate). However, the HTML report does expose URL paths, partial IP addresses, and traffic statistics. Always protect the dashboard behind HTTP Basic Auth or restrict it to internal network access before exposing it externally.
How is GoAccess different from Matomo or Google Analytics?
GoAccess analyzes server-side access logs, so it captures every request including bots, crawlers, and API calls that browser-based analytics tools miss. However, it doesn't provide session tracking, conversion funnels, or detailed user journey data like Matomo does. Use GoAccess for server health monitoring and security analysis; use Matomo or similar tools for user behavior analytics.