Self-Hosting GoatCounter with an nginx Reverse Proxy and SSL
Being curious about the traffic on this site and the tabi demo, I researched ethical analytics tools. My goal was to satisfy my curiosity without compromising the privacy of visitors; I’m against tracking and I don’t want to contribute to surveillance capitalism.
I found GoatCounter, a lightweight, open-source, privacy-friendly web analytics tool. It’s a single binary and it can be self-hosted. A perfect fit!
The self-hosting instructions for GoatCounter are sparse. There’s this script that automatically downloads and sets up GoatCounter. However, I’m not comfortable running scripts without examining them first, particularly when they’re run as root.
The guide that follows is based on the script, with extra steps for an nginx reverse proxy with SSL certificates. If you’re on a systemd Linux distribution (Debian, Arch, Ubuntu…), you should be able to follow along.
By the end of this article, you’ll have a dashboard like stats.osc.garden or tabi-stats.osc.garden (it’s up to you whether to make it public).
Let’s begin!
Download and Install
We’ll need root permissions for everything we’re about to do, so let’s start a root shell:
Download the GoatCounter binary. Get the URL for your system from the GoatCounter GitHub releases page.
I used:
If you’re unsure about which system you’re on, you can run the arch
command. If you get x86_64
, you’ll need the amd64
package, like me.
Unzip the file:
Move it to /usr/local/bin/
and set the appropriate permissions:
Let’s create a specific user to run GoatCounter, for security isolation:
Now run goatcounter
to set up our user and credentials. Make sure to use your actual e-mail and URL (where the dashboard will live, not the site you want to track):
This will ask for a password for the GoatCounter dashboard.
nginx Setup with SSL
First you’ll need to configure your DNS for your URL (stats.example.com
) and get the TLS certificate. I used Let’s Encrypt’s certbot
for the certificate.
Now it’s time to set up the nginx reverse proxy. Here’s what the site config should look like:
server {
if ($host = stats.example.com) {
301 ;
}
80;
.example.com;
stats301 ;
}
server {
443 ssl http2;
.example.com;
stats
/etc/letsencrypt/live/stats.example.com/fullchain.pem;
/etc/letsencrypt/live/stats.example.com/privkey.pem;
location / {
;
$host;
Host X-Real-IP $remote_addr;
X-Forwarded-For $proxy_add_x_forwarded_for;
X-Forwarded-Proto $scheme;
}
}
Modify the $host
/server_name
and ssl_certificate
/ssl_certificate_key
paths to match your URL and certificate paths.
In the proxy_pass
directive, set your preferred port and use it for the next steps.
Starting GoatCounter
Check the nginx config has no issues and reload it:
&&
Let’s see if goatcounter
works:
You should see:
Dec 14 13:18:47 INFO: ready; serving 1 sites on ":8091"; dev=false; sites: stats.example.com
And you should be able to log in to the dashboard on the URL you chose.
If everything’s working, good job! Now it’s time to set up a systemd service file. This will help us easily start/stop goatcounter
and automate its (re)start on boot/failure.
systemd Service File
Still from the root shell, we create the file with:
Here’s a good starting point for this file. For the ExecStart=
line, use the same command we just tested:
[Unit]
GoatCounter web analytics
[Service]
simple
always
/var/lib/goatcounter
/usr/local/bin/goatcounter serve -listen :8091 -tls none
goatcounter
goatcounter
true
true
true
closed
[Install]
-user.target
multi
Save the file and exit the editor.
Starting the Service
Now we’ll reload systemd and start the service:
At this point, you should be able to access your dashboard on the URL you chose. You can check the service status with systemctl status goatcounter
. To see the logs, use journalctl -fu goatcounter
.
Finally, enable the service to start on boot:
That’s it! Now goatcounter
will start on boot and restart if it fails. Happy analytics!
If you want to support the GoatCounter project, you can sponsor Martin Tournoij, its creator and maintainer, on GitHub Sponsors.