goaccess-for-nginxproxymanager: a GoAccess dashboard for proxy logs in Docker
GoAccess Docker Image for Nginx Proxy Manager and more...
At a glance
- What is it?
- A container image that turns Nginx Proxy Manager, Traefik, Caddy or custom access logs into a GoAccess web report, with the log format chosen by an environment variable and the parser configuration baked into the image. It is a thin wrapper, which is both its strength and its limit.
- Who is it for?
- Adopt goaccess-for-nginxproxymanager if you already run Nginx Proxy Manager or Traefik on Docker and want a traffic dashboard without wiring GoAccess yourself. Skip it if you need per-request search, a query interface or alerting, since a periodic HTML report is all this image produces, and skip it if you want a maintained upstream.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 173 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem is that proxy logs are scattered and the formats differ
GoAccess is a log analyser that turns web server access logs into an interactive report. The hard part in a proxy setup is not GoAccess itself, it is that the logs you want to analyse are spread across per-host files, in a format that is not the combined format GoAccess expects by default.
Nginx Proxy Manager writes a separate access log per proxy host, named after the host number, and it rotates them into archives. The image exists to bridge that gap. It parses the following log types: NPM, NPM Redirection, NPM Error, Traefik and Caddy, and you can load your own custom config as well to parse other logs.
The audience is therefore operators of a self hosted reverse proxy who want to see what is happening across all their hosts at once, rather than opening a dozen log files. The project description is short: GoAccess Docker Image for Nginx Proxy Manager and more.
It is worth being clear at the outset that this is a wrapper, not a GoAccess fork. The GoAccess version it depends on is 1.10.2, the image ships the MaxMind GeoLite2 databases for City, Country and ASN, all dated 2026-04-30, and the container serves the report on a single port. Everything that makes GoAccess a good analyser, and everything that makes it limited, comes from GoAccess.
The project states plainly that it is still in development, and that you might need to wait a bit if you have a large amount of logs for it to parse. That sentence is the most important operational fact in the readme.
The compose file is the whole configuration surface
There is no configuration file to edit and no dashboard to learn. The image is driven entirely by environment variables, and the readme gives a complete compose file you can paste.
The core of it looks like this:
services:
goaccess:
image: 'xavierh/goaccess-for-nginxproxymanager:latest'
container_name: goaccess
restart: always
ports:
- '7880:7880'
environment:
- TZ=America/New_York
- SKIP_ARCHIVED_LOGS=False #optional
- DEBUG=False #optionaland the volumes are the part that matters, because the container reads the logs from a mounted path:
volumes:
- /path/to/host/nginx/logs:/opt/log:ro
- /path/to/host/custom:/opt/custom:ro #optional, required if using log_type = CUSTOM
- goaccess_cache:/var/cache/goaccess #optional, required if using DB_PATHThere is a named volume for the GoAccess database, so that parsed data survives a container restart, and the log mount is read only, which is the right way to give a container access to host files.
The log format is a single variable, LOG_TYPE, and the readme lists the options as CUSTOM, NPM, NPM+R, TRAEFIK, NCSA_COMBINED and CADDY_V1, with NPM as the default configuration. Two variables narrow the scope further. SKIP_ARCHIVED_LOGS set to True skips the archived logs, that is proxy-host*.gz, which is what you want if you do not want to reprocess history. INCLUDE_PROXY_HOSTS takes a comma separated list of proxy host numbers, for example 11,21, so that it consumes only proxy-host-11_access.log* and proxy-host-21_access.log*, and the readme explains that the host number is found by right clicking on the three dots on the proxy host line in the GUI.
That INCLUDE_PROXY_HOSTS variable is the one to reach for first in a real deployment. A shared proxy accumulates a lot of hosts, and analysing all of them when you care about two is a slow way to get an answer.
The Dockerfile shows the two builds and the patches underneath
The Dockerfile is short and it explains a surprising amount about what this image does to GoAccess.
It is a two-stage build on alpine. The builder stage installs build-base, libmaxminddb-dev, ncurses-dev, musl-locales and gettext-dev, then downloads the GoAccess source tarball from the repository's own assests directory, goaccess-1.10.2.tar.gz, and unpacks it twice into separate directories. The first build is the debug variant, configured with --enable-utf8 --enable-geoip=mmdb --with-getline --enable-debug, and the second is the release variant with the same flags minus the debug one.
Two sed patches are applied to the release build, and they are the interesting part. The first rewrites the template footer to add a link and the version string v1.1.40, and the second changes bottom: 190px to bottom: 260px in the stylesheet. So the image is not merely repackaging GoAccess, it is patching its templates to accommodate the extra navigation this image adds.
The runtime stage installs bash, nginx, tini, wget, curl, apache2-utils, libmaxminddb, tzdata, gettext, musl-locales and ncurses, removes the stock nginx.conf, copies both the debug and release GoAccess trees across, copies the locale files, and then stages the configuration: the goaccess.conf is copied in as goaccess.conf.bak, and the three MaxMind databases, GeoLite2-City.mmdb, GeoLite2-ASN.mmdb and GeoLite2-Country.mmdb, are copied from the assests/maxmind directory.
The nginx and tini installation is the reason the image serves on a port at all. GoAccess can serve its own report, but wrapping it in nginx is what turns a single binary into something that can sit behind basic auth and behave like a normal web service. The runtime installs apache2-utils, which is the package that provides the htpasswd tool, and that is why the BASIC_AUTH option works.
Basic auth, IP filtering and a browser list are all environment variables
Three groups of settings turn a report endpoint into something you would expose on a network.
The first is access control. BASIC_AUTH enables nginx basic authentication and requires BASIC_AUTH_USERNAME and BASIC_AUTH_PASSWORD, with the credentials shown as user and pass in the example compose file. The readme flags a sharp edge: the Docker container needs to be stopped or restarted each time this flag is modified, and this allows for the .htpasswd file to be changed accordingly. That is a consequence of generating the htpasswd file at container start, and it means changing a password is a restart, not a reload.
The second is what you do not want to count. EXCLUDE_IPS takes IP addresses or ranges delimited by comma, with the readme pointing at the GoAccess manual for the range syntax and giving examples like 192.168.0.1-192.168.0.100 or 127.0.0.1,192.168.0.1-192.168.0.100. This is the setting for excluding your own infrastructure and the health checks that would otherwise dominate the top-10 lists.
The third is the browser list. ENABLE_BROWSERS_LIST turns the feature on, and CUSTOM_BROWSERS supplies a comma delimited list of name and label pairs, with the example Kuma:Uptime,TestBrowser:Crawler. That is aimed at Uptime Kuma and other monitoring probes that poll your services on a schedule and would otherwise appear as a meaningful share of your traffic.
Two more settings control the report itself. HTML_REFRESH sets the number of seconds between refreshes of the HTML report, and KEEP_LAST sets the number of days of data to keep in storage. Both link to the GoAccess manual for the full parameter reference, which is a good pointer since the image exposes the underlying tool's options directly.
The pattern is that this image does not add an abstraction layer over GoAccess. It selects a log format, starts the analyser, and serves the result.
Persistence and threading, and the settings that decide whether it keeps up
Three variables decide how the image behaves over time, and they are worth reading together.
PERSIST persists parsed data to disk, and RESTORE loads previously stored data from disk. Both are enabled in the example compose file, and both are needed for the DB_PATH setting to make sense. DB_PATH is where the on-disk database files are stored, defaulting to /tmp, and the compose file points it at /var/cache/goaccess, which is the named volume. If you set DB_PATH to a path that is not a mounted volume, you get a container that forgets everything when it is recreated, and a cache volume is listed in the compose file as required if using DB_PATH.
PROCESSING_THREADS sets the number of concurrent processing threads in the program's execution, affecting log data analysis, and the readme notes it is typically adjusted based on CPU cores, with a default of 1. That default of 1 is the reason the project warns you might need to wait if you have a large amount of logs to parse. Single threaded parsing is simple and predictable, and it is also the bottleneck the moment your proxy starts serving real traffic.
The tuning advice follows directly. If the dashboard takes too long to appear, the levers are PROCESSING_THREADS, KEEP_LAST so that the analyser is not reprocessing history, INCLUDE_PROXY_HOSTS so that it is not reading hosts you do not care about, and SKIP_ARCHIVED_LOGS so that it is not decompressing archives on every start.
The other operational setting is the timezone, TZ, which the example sets to America/New_York. That matters more than it looks: the report groups by hour, and a container running in UTC while your logs are in local time produces a dashboard that is several hours off from what you expect.
If you hit permission errors reading the mounted logs, the readme's answer is to add PUID and PGID with the correct user id that has read access to the log files, and the example sets both to 0, which is root and works because the container is running as root.
Where this image stops being the right answer
There are a few situations where this is the wrong tool, and the main one is about what kind of tool it is.
The first is that GoAccess is a report generator, not an analytics server. The image produces an HTML report that refreshes on a timer, HTML_REFRESH being the interval. It is a dashboard you look at, not a system you query. If you need to answer arbitrary questions about your traffic, filter by any combination of fields, or build a historical view, you are looking for something with a query interface rather than a periodic report.
The second is alerting and monitoring integration. There is no webhook, no scheduled report to email, and no way to get notified when something changes. For an operation where the log data itself is the signal, such as watching for a spike in errors, you would need to build that on top of the same data rather than on top of this image.
The third is the upstream dependency. The GoAccess version is pinned at 1.10.2 in the image, and the parse behaviour of your logs is therefore the behaviour of that version, not of the current GoAccess. If a new GoAccess release fixes a parsing problem you have, you would be waiting for this image to pick it up. The image is versioned separately, with tags like v1.1.40, v1.1.38 and v1.1.37, and the release dates are recent relative to each other, but the point stands: you are on someone else's schedule twice over.
The fourth is the security surface of a stats page. Basic auth over the report is a reasonable protection, and the readme is clear that enabling it requires a restart, but the credentials sit in the compose file as environment variables, which means they are in whatever version control that file is in. For a shared proxy whose logs may contain paths and query strings, treating those logs as sensitive is the right default.
And the fifth is simply the development status. The project says it is still in development, and the readme opens with a note that because of other priorities and projects the code will be updated at least once a month. The repository is not archived, the last push was on 2026-05-03, and the most recent release is v1.1.40 from the same day, so the project is being worked on at the pace the note describes, which is a fair expectation to set but not a guarantee of anything specific.
If you need durable, queryable traffic history with alerting, the honest alternative is to run your proxy logs into a system built for that, and keep this image for the at a glance view.
Editorial conclusion
Adopt goaccess-for-nginxproxymanager if you already run Nginx Proxy Manager or Traefik on Docker and want a traffic dashboard without wiring GoAccess yourself. Skip it if you need per-request search, a query interface or alerting, since a periodic HTML report is all this image produces, and skip it if you want a maintained upstream. Verify first that your log files are mounted where the container expects them, that the LOG_TYPE you choose matches your actual log format, and that the container can read them, using PUID and PGID if the host paths are not world readable.
Frequently asked questions
Which log formats can goaccess-for-nginxproxymanager parse?
The LOG_TYPE variable accepts CUSTOM, NPM, NPM+R, TRAEFIK, NCSA_COMBINED and CADDY_V1, with NPM as the default. The custom config directory is mounted at /opt/custom and is required when LOG_TYPE is CUSTOM.
How do I run goaccess-for-nginxproxymanager with Docker Compose?
The readme gives a complete compose file. It publishes port 7880, mounts your host log directory read only at /opt/log, and mounts a named volume at /var/cache/goaccess for the persisted database, with the format selected through the LOG_TYPE environment variable.
How do I make the GoAccess dashboard require a login?
Set BASIC_AUTH=True along with BASIC_AUTH_USERNAME and BASIC_AUTH_PASSWORD. The readme notes that the container needs to be stopped or restarted each time this flag is modified, which allows for the .htpasswd file to be changed accordingly.
Why is my goaccess-for-nginxproxymanager dashboard slow to appear?
The readme warns that you might need to wait if you have a large amount of logs to parse, and PROCESSING_THREADS defaults to 1 concurrent processing thread. You can raise it based on CPU cores, and reduce the work by setting KEEP_LAST, INCLUDE_PROXY_HOSTS or SKIP_ARCHIVED_LOGS.
How do I exclude my own IPs and monitoring probes from the report?
Use EXCLUDE_IPS with comma delimited addresses or ranges, for example 192.168.0.1-192.168.0.100. For scheduled probes, ENABLE_BROWSERS_LIST turns the browser list on and CUSTOM_BROWSERS takes a comma delimited list of name and label pairs such as Kuma:Uptime,TestBrowser:Crawler.
Which GoAccess version and GeoIP databases does the image ship?
The image depends on GoAccess version 1.10.2 and bundles the GeoLite2-City, GeoLite2-Country and GeoLite2-ASN MaxMind databases, dated 2026-04-30. The most recent release is v1.1.40 from 2026-05-03, the same day as the last push, and the licence is MIT.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/xavier-hernandez-goaccess-for-nginxproxymanager)Was this useful?